diff --git a/README.md b/README.md index bfdc3e67..55373f99 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ You can try the [live demo](http://edenspiekermann.github.io/accessible-modal-di - Addition of `[tabindex]:not([value="-1"])` to focusable elements; - No more `display` manipulation in JS, the hiding mechanism is entirely up to the CSS layer (using `[aria-hidden]` selectors); - Full test coverage with [CasperJS](http://casperjs.org) and [CodeShip](https://codeship.com); -- Cleaner code resulting in only 700 bytes (0.7Kb!) once gzipped. +- Cleaner code resulting in only 650 bytes (0.65Kb!) once gzipped. *Note: the script should run seamlessly in Internet Explorer 9 and above.* diff --git a/accessible-modal-dialog.js b/accessible-modal-dialog.js index a3191c01..2349cc42 100644 --- a/accessible-modal-dialog.js +++ b/accessible-modal-dialog.js @@ -30,40 +30,6 @@ } } - // Helper function to bind listeners for a Modal instance - function bindListeners (instance) { - instance.$openers.forEach(function ($opener) { - $opener.addEventListener('click', function () { - instance.show(); - }); - }); - - instance.$closers.forEach(function ($closer) { - $closer.addEventListener('click', function () { - instance.hide(); - }); - }); - - document.addEventListener('keydown', function (event) { - if (instance.shown === false) return; - - if (event.which === 27) { - event.preventDefault(); - instance.hide(); - } - - if (event.which === 9) { - trapTabKey(instance.$node, event); - } - }); - - document.body.addEventListener('focus', function (event) { - if (instance.shown && !instance.$node.contains(event.target)) { - setFocusToFirstItem(instance.$node); - } - }, true); - } - // Helper function to focus first focusable item in node function setFocusToFirstItem (node) { var focusableChildren = getFocusableChildren(node); @@ -78,39 +44,52 @@ * @param {Node} main - Main element of the page */ var Modal = function (node, main) { - this.$main = main || document.querySelector('#main'); - this.$node = node; - this.$openers = $$('[data-modal-show="' + this.$node.id + '"]'); - this.$closers = $$('[data-modal-hide]', this.$node) - .concat($$('[data-modal-hide="' + this.$node.id + '"]')); + var that = this; + main = main || document.querySelector('#main'); this.shown = false; - bindListeners(this); - }; + $$('[data-modal-show="' + node.id + '"]').forEach(function (opener) { + opener.addEventListener('click', show); + }); - /** - * Method to display the modal - */ - Modal.prototype.show = function () { - this.shown = true; + $$('[data-modal-hide]', node).concat($$('[data-modal-hide="' + node.id + '"]')).forEach(function (closer) { + closer.addEventListener('click', hide); + }); - this.$node.setAttribute('aria-hidden', 'false'); - this.$main.setAttribute('aria-hidden', 'true'); + document.addEventListener('keydown', function (event) { + if (that.shown && event.which === 27) { + event.preventDefault(); + hide(); + } - focusedElementBeforeModal = document.activeElement; - setFocusToFirstItem(this.$node); - }; + if (that.shown && event.which === 9) { + trapTabKey(node, event); + } + }); - /** - * Method to hide the modal - */ - Modal.prototype.hide = function () { - this.shown = false; + document.body.addEventListener('focus', function (event) { + if (that.shown && !node.contains(event.target)) { + setFocusToFirstItem(node); + } + }, true); + + this.show = show; + this.hide = hide; - this.$node.setAttribute('aria-hidden', 'true'); - this.$main.setAttribute('aria-hidden', 'false'); + function show () { + that.shown = true; + node.setAttribute('aria-hidden', 'false'); + main.setAttribute('aria-hidden', 'true'); + focusedElementBeforeModal = document.activeElement; + setFocusToFirstItem(node); + } - focusedElementBeforeModal.focus(); + function hide () { + that.shown = false; + node.setAttribute('aria-hidden', 'true'); + main.setAttribute('aria-hidden', 'false'); + focusedElementBeforeModal.focus(); + } }; global.Modal = Modal; diff --git a/accessible-modal-dialog.min.js b/accessible-modal-dialog.min.js index 2323c9ed..ce9d9aeb 100644 --- a/accessible-modal-dialog.min.js +++ b/accessible-modal-dialog.min.js @@ -1 +1 @@ -!function(t){"use strict";function e(t){var e=["a[href]","area[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","iframe","object","embed","[contenteditable]",'[tabindex]:not([tabindex="-1"])'];return n(e.join(","),t).filter(function(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)})}function n(t,e){var n=(e||document).querySelectorAll(t);return n.length?Array.prototype.slice.call(n):[]}function i(t,n){var i=e(t),o=i.indexOf(document.activeElement);n.shiftKey&&0===o?(i[i.length-1].focus(),n.preventDefault()):n.shiftKey||o!==i.length-1||(i[0].focus(),n.preventDefault())}function o(t){t.$openers.forEach(function(e){e.addEventListener("click",function(){t.show()})}),t.$closers.forEach(function(e){e.addEventListener("click",function(){t.hide()})}),document.addEventListener("keydown",function(e){t.shown!==!1&&(27===e.which&&(e.preventDefault(),t.hide()),9===e.which&&i(t.$node,e))}),document.body.addEventListener("focus",function(e){t.shown&&!t.$node.contains(e.target)&&d(t.$node)},!0)}function d(t){var n=e(t);n.length&&n[0].focus()}var s,a=function(t,e){this.$main=e||document.querySelector("#main"),this.$node=t,this.$openers=n('[data-modal-show="'+this.$node.id+'"]'),this.$closers=n("[data-modal-hide]",this.$node).concat(n('[data-modal-hide="'+this.$node.id+'"]')),this.shown=!1,o(this)};a.prototype.show=function(){this.shown=!0,this.$node.setAttribute("aria-hidden","false"),this.$main.setAttribute("aria-hidden","true"),s=document.activeElement,d(this.$node)},a.prototype.hide=function(){this.shown=!1,this.$node.setAttribute("aria-hidden","true"),this.$main.setAttribute("aria-hidden","false"),s.focus()},t.Modal=a}(window); \ No newline at end of file +!function(t){"use strict";function e(t){var e=["a[href]","area[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","iframe","object","embed","[contenteditable]",'[tabindex]:not([tabindex="-1"])'];return n(e.join(","),t).filter(function(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)})}function n(t,e){var n=(e||document).querySelectorAll(t);return n.length?Array.prototype.slice.call(n):[]}function i(t,n){var i=e(t),o=i.indexOf(document.activeElement);n.shiftKey&&0===o?(i[i.length-1].focus(),n.preventDefault()):n.shiftKey||o!==i.length-1||(i[0].focus(),n.preventDefault())}function o(t){var n=e(t);n.length&&n[0].focus()}var a,d=function(t,e){function d(){r.shown=!0,t.setAttribute("aria-hidden","false"),e.setAttribute("aria-hidden","true"),a=document.activeElement,o(t)}function c(){r.shown=!1,t.setAttribute("aria-hidden","true"),e.setAttribute("aria-hidden","false"),a.focus()}var r=this;e=e||document.querySelector("#main"),this.shown=!1,n('[data-modal-show="'+t.id+'"]').forEach(function(t){t.addEventListener("click",d)}),n("[data-modal-hide]",t).concat(n('[data-modal-hide="'+t.id+'"]')).forEach(function(t){t.addEventListener("click",c)}),document.addEventListener("keydown",function(e){r.shown&&27===e.which&&(e.preventDefault(),c()),r.shown&&9===e.which&&i(t,e)}),document.body.addEventListener("focus",function(e){r.shown&&!t.contains(e.target)&&o(t)},!0),this.show=d,this.hide=c};t.Modal=d}(window); \ No newline at end of file diff --git a/example/accessible-modal-dialog.js b/example/accessible-modal-dialog.js index a3191c01..2349cc42 100644 --- a/example/accessible-modal-dialog.js +++ b/example/accessible-modal-dialog.js @@ -30,40 +30,6 @@ } } - // Helper function to bind listeners for a Modal instance - function bindListeners (instance) { - instance.$openers.forEach(function ($opener) { - $opener.addEventListener('click', function () { - instance.show(); - }); - }); - - instance.$closers.forEach(function ($closer) { - $closer.addEventListener('click', function () { - instance.hide(); - }); - }); - - document.addEventListener('keydown', function (event) { - if (instance.shown === false) return; - - if (event.which === 27) { - event.preventDefault(); - instance.hide(); - } - - if (event.which === 9) { - trapTabKey(instance.$node, event); - } - }); - - document.body.addEventListener('focus', function (event) { - if (instance.shown && !instance.$node.contains(event.target)) { - setFocusToFirstItem(instance.$node); - } - }, true); - } - // Helper function to focus first focusable item in node function setFocusToFirstItem (node) { var focusableChildren = getFocusableChildren(node); @@ -78,39 +44,52 @@ * @param {Node} main - Main element of the page */ var Modal = function (node, main) { - this.$main = main || document.querySelector('#main'); - this.$node = node; - this.$openers = $$('[data-modal-show="' + this.$node.id + '"]'); - this.$closers = $$('[data-modal-hide]', this.$node) - .concat($$('[data-modal-hide="' + this.$node.id + '"]')); + var that = this; + main = main || document.querySelector('#main'); this.shown = false; - bindListeners(this); - }; + $$('[data-modal-show="' + node.id + '"]').forEach(function (opener) { + opener.addEventListener('click', show); + }); - /** - * Method to display the modal - */ - Modal.prototype.show = function () { - this.shown = true; + $$('[data-modal-hide]', node).concat($$('[data-modal-hide="' + node.id + '"]')).forEach(function (closer) { + closer.addEventListener('click', hide); + }); - this.$node.setAttribute('aria-hidden', 'false'); - this.$main.setAttribute('aria-hidden', 'true'); + document.addEventListener('keydown', function (event) { + if (that.shown && event.which === 27) { + event.preventDefault(); + hide(); + } - focusedElementBeforeModal = document.activeElement; - setFocusToFirstItem(this.$node); - }; + if (that.shown && event.which === 9) { + trapTabKey(node, event); + } + }); - /** - * Method to hide the modal - */ - Modal.prototype.hide = function () { - this.shown = false; + document.body.addEventListener('focus', function (event) { + if (that.shown && !node.contains(event.target)) { + setFocusToFirstItem(node); + } + }, true); + + this.show = show; + this.hide = hide; - this.$node.setAttribute('aria-hidden', 'true'); - this.$main.setAttribute('aria-hidden', 'false'); + function show () { + that.shown = true; + node.setAttribute('aria-hidden', 'false'); + main.setAttribute('aria-hidden', 'true'); + focusedElementBeforeModal = document.activeElement; + setFocusToFirstItem(node); + } - focusedElementBeforeModal.focus(); + function hide () { + that.shown = false; + node.setAttribute('aria-hidden', 'true'); + main.setAttribute('aria-hidden', 'false'); + focusedElementBeforeModal.focus(); + } }; global.Modal = Modal;