storm-modal
Version:
Accessible modal dialogue
132 lines (117 loc) • 4.27 kB
JavaScript
/**
* @name storm-modal: Accessible modal dialogue
* @version 1.2.0: Thu, 02 Aug 2018 12:23:05 GMT
* @author stormid
* @license MIT
*/
(function(root, factory) {
var mod = {
exports: {}
};
if (typeof exports !== 'undefined'){
mod.exports = exports
factory(mod.exports)
module.exports = mod.exports.default
} else {
factory(mod.exports);
root.StormModal = mod.exports.default
}
}(this, function(exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var defaults = {
onClassName: 'active',
mainSelector: 'main',
modalSelector: 'js-modal',
callback: false
};
var TRIGGER_EVENTS = window.PointerEvent ? ['pointerdown', 'keydown'] : ['ontouchstart' in window ? 'touchstart' : 'click', 'keydown'];
var TRIGGER_KEYCODES = [13, 32];
var FOCUSABLE_ELEMENTS = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'iframe', 'object', 'embed', '[contenteditable]', '[tabindex]:not([tabindex="-1"])'];
var componentPrototype = {
init: function init() {
this.isOpen = false;
this.dialog = this.node.querySelector('[role=dialog]');
if (!this.dialog) throw new Error('Modal cannot be initialised, a modal must contain a dialog (role="dialog")');
this.togglers = this.node.getAttribute('data-modal-toggler') && [].slice.call(document.querySelectorAll('.' + this.node.getAttribute('data-modal-toggler')));
if (!this.togglers.length) throw new Error('Modal cannot be initialised, no modal toggler elements found');
this.boundKeyListener = this.keyListener.bind(this);
this.initTriggers();
this.focusableChildren = this.getFocusableChildren();
this.dialog.setAttribute('aria-hidden', true);
return this;
},
initTriggers: function initTriggers() {
var _this = this;
this.togglers.forEach(function (toggler) {
TRIGGER_EVENTS.forEach(function (ev) {
toggler.addEventListener(ev, function (e) {
if (!!e.keyCode && !~TRIGGER_KEYCODES.indexOf(e.keyCode) || e.which && e.which === 3) return;
e.preventDefault();
_this.change(_this);
});
});
});
},
getFocusableChildren: function getFocusableChildren() {
return [].slice.call(this.node.querySelectorAll(FOCUSABLE_ELEMENTS.join(',')));
},
trapTab: function trapTab(e) {
var focusedIndex = this.focusableChildren.indexOf(document.activeElement);
if (e.shiftKey && focusedIndex === 0) {
e.preventDefault();
this.focusableChildren[this.focusableChildren.length - 1].focus();
} else {
if (!e.shiftKey && focusedIndex === this.focusableChildren.length - 1) {
e.preventDefault();
this.focusableChildren[0].focus();
}
}
},
keyListener: function keyListener(e) {
if (this.isOpen && e.keyCode === 27) {
e.preventDefault();
this.toggle();
}
if (this.isOpen && e.keyCode === 9) this.trapTab(e);
},
open: function open() {
var _this2 = this;
document.addEventListener('keydown', this.boundKeyListener);
this.lastFocused = document.activeElement;
this.focusableChildren.length && window.setTimeout(function () {
_this2.focusableChildren[0].focus();
}, 0);
this.toggle();
},
close: function close() {
document.removeEventListener('keydown', this.boundKeyListener);
this.lastFocused.focus();
this.toggle();
},
toggle: function toggle() {
this.isOpen = !this.isOpen;
this.dialog.setAttribute('aria-hidden', !this.isOpen);
this.node.classList.toggle(this.settings.onClassName);
// document.querySelector(this.settings.mainSelector) && document.querySelector(this.settings.mainSelector).setAttribute('aria-hidden', this.isOpen);
},
change: function change() {
if (!this.isOpen) this.open();else this.close();
typeof this.settings.callback === 'function' && this.settings.callback.call(this);
}
};
var init = function init(sel, opts) {
var els = [].slice.call(document.querySelectorAll(sel));
if (els.length === 0) throw new Error('Modal cannot be initialised, no trigger elements found');
return els.map(function (el) {
return Object.assign(Object.create(componentPrototype), {
node: el,
settings: Object.assign({}, defaults, opts)
}).init();
});
};
var index = { init: init };
exports.default = index;;
}));