accessible-toggle
Version:
Accessible and responsive toggling of an element's visibility
2 lines (1 loc) • 5.68 kB
JavaScript
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).accessibleToggle=e()}(this,function(){"use strict";var n=function(o){function t(){for(var t,e=arguments.length,n=new Array(e),i=0;i<e;i++)n[i]=arguments[i];s=n,null===a&&(a=requestAnimationFrame((t=this,function(){a=null,o.apply(t,s)})))}var s,a=null;return t.cancel=function(){cancelAnimationFrame(a),a=null},t},i={trapFocus:!0,assignFocus:!0,closeOnEsc:!0,closeOnClickOutside:!1,mediaQuery:!1,onShow:function(){},onHide:function(){},onEnable:function(){},onDisable:function(){}},o=['a[href]:not([tabindex^="-"]):not([inert])','area[href]:not([tabindex^="-"]):not([inert])',"input:not([disabled]):not([inert])","select:not([disabled]):not([inert])","textarea:not([disabled]):not([inert])","button:not([disabled]):not([inert])",'iframe:not([tabindex^="-"]):not([inert])','[contenteditable]:not([tabindex^="-"]):not([inert])','[tabindex]:not([tabindex^="-"]):not([inert])'],s=9,a=27;function r(t,e){t=(e||document).querySelectorAll(t);return Array.prototype.slice.call(t)}return function(){function t(t,e){void 0===e&&(e={}),t&&t instanceof HTMLElement?(this.content=t,this.id=t.id,this.buttons=r("[data-toggle='"+this.id+"']"),this.focusableChildren=this.getFocusableChildElements(),this.options=Object.assign({},i,e),this.throttledMediaQueryTest=n(this.testMediaQuery.bind(this)),0!==this.buttons.length?this.setup():console.warn("Toggle: there are no buttons that control the toggleable element.")):console.warn("Toggle: first parameter must by an HTML element.")}var e=t.prototype;return e.setup=function(){!1===this.options.mediaQuery?this.enable():(this.testMediaQuery(),window.addEventListener("resize",this.throttledMediaQueryTest))},e.destroy=function(){window.removeEventListener("resize",this.throttledMediaQueryTest),this.disable()},e.enable=function(){var t,n=this;this.active||(this.boundClickHandler=this.clickHandler.bind(this),this.boundKeypressHandler=this.keypressHandler.bind(this),document.addEventListener("keydown",this.boundKeypressHandler),document.addEventListener("click",this.boundClickHandler),this.content.setAttribute("aria-labelledby",this.id+"-control-0"),this.buttons.forEach(function(t,e){t.setAttribute("aria-controls",n.id),t.setAttribute("id",n.id+"-control-"+e)}),this.content.hasAttribute("data-toggle-open")?this.show():this.hide(),"function"==typeof this.options.onEnable&&this.options.onEnable(),t=new Event("toggle-enable"),this.content.dispatchEvent(t),this.active=!0)},e.disable=function(){var t;this.active&&(document.removeEventListener("click",this.boundClickHandler),document.removeEventListener("keyup",this.boundKeyupHandler),this.buttons.forEach(function(t){t.removeAttribute("aria-label"),t.removeAttribute("aria-expanded"),t.removeAttribute("aria-controls"),t.removeAttribute("id")}),this.content.removeAttribute("aria-hidden"),this.content.removeAttribute("aria-labelledby"),this.focusableChildren.forEach(function(t){t.hasAttribute("data-toggle-tabindex")?(t.setAttribute("tabindex",t.getAttribute("data-toggle-tabindex")),t.removeAttribute("data-toggle-tabindex")):t.removeAttribute("tabindex")}),"function"==typeof this.options.onDisable&&this.options.onDisable(),t=new Event("toggle-disable"),this.content.dispatchEvent(t),this.active=!1)},e.testMediaQuery=function(){this.options.mediaQuery&&window.matchMedia(this.options.mediaQuery).matches?this.enable():this.disable()},e.isOpen=function(){return"true"!==this.content.getAttribute("aria-hidden")},e.show=function(){this.content.setAttribute("aria-hidden","false"),this.buttons.forEach(function(t){t.setAttribute("aria-expanded","true")}),this.focusableChildren.forEach(function(t){t.hasAttribute("data-toggle-tabindex")?t.setAttribute("tabindex",t.getAttribute("data-toggle-tabindex")):t.removeAttribute("tabindex")}),!this.options.assignFocus||(t=this.content.querySelector("[autofocus]")||this.focusableChildren[0])&&t.focus(),"function"==typeof this.options.onShow&&this.options.onShow();var t=new Event("toggle-show");return this.content.dispatchEvent(t),this},e.hide=function(){this.content.setAttribute("aria-hidden","true"),this.buttons.forEach(function(t){t.setAttribute("aria-expanded","false")}),this.focusableChildren.forEach(function(t){var e=t.getAttribute("tabindex");e&&(t.dataset.toggleTabindex=e),t.setAttribute("tabindex","-1")}),"function"==typeof this.options.onShow&&this.options.onHide();var t=new Event("toggle-hide");return this.content.dispatchEvent(t),this},e.toggle=function(t){return void 0===t&&(t=!this.isOpen()),t?this.show():this.hide(),this},e.clickHandler=function(t){for(var e=t.target;e&&1===e.nodeType;){if(this.buttons.includes(e))return t.preventDefault(),void this.toggle();e=e.parentNode}this.options.closeOnClickOutside&&this.isOpen()&&this.content!==t.target&&!this.content.contains(t.target)&&(t.preventDefault(),this.hide())},e.keypressHandler=function(t){this.options.closeOnEsc&&this.isOpen()&&t.which===a&&(t.preventDefault(),this.hide(),this.buttons[0].focus()),this.options.trapFocus&&t.which===s&&this.trapFocus(t)},e.getFocusableChildElements=function(){return r(o.join(","),this.content).filter(function(t){return Boolean(t.offsetWidth||t.offsetHeight||t.getClientRects().length)})},e.trapFocus=function(t){var e;0<this.focusableChildren.length&&(e=this.focusableChildren.indexOf(document.activeElement)||0,t.shiftKey||e!==this.focusableChildren.length-1||(this.focusableChildren[0].focus(),t.preventDefault()),t.shiftKey&&0===e&&(this.focusableChildren[this.focusableChildren.length-1].focus(),t.preventDefault()))},t}()});