a11y-dialog
Version:
A tiny script to make dialog windows accessible to assistive technology users.
3 lines (2 loc) • 4.79 kB
JavaScript
/*! a11y-dialog 8.1.4 — © Kitty Giraudel */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).A11yDialog=t()}(this,(function(){"use strict";const e=":not([inert]):not([inert] *)",t=':not([tabindex^="-"])',i=":not(:disabled)";var s=[`a[href]${e}${t}`,`area[href]${e}${t}`,`input:not([type="hidden"]):not([type="radio"])${e}${t}${i}`,`input[type="radio"]${e}${t}${i}`,`select${e}${t}${i}`,`textarea${e}${t}${i}`,`button${e}${t}${i}`,`details${e} > summary:first-of-type${t}`,`iframe${e}${t}`,`audio[controls]${e}${t}`,`video[controls]${e}${t}`,`[contenteditable]${e}${t}`,`[tabindex]${e}${t}`];function n(e){(e.querySelector("[autofocus]")||e).focus()}function o(e,t){if(t&&h(e))return e;if(function(e){return(!e.shadowRoot||"-1"!==e.getAttribute("tabindex"))&&!e.matches(":disabled,[hidden],[inert]")}(e))if(e.shadowRoot){let i=r(e.shadowRoot,t);for(;i;){const e=o(i,t);if(e)return e;i=d(i,t)}}else if("slot"===e.localName){const i=e.assignedElements({flatten:!0});t||i.reverse();for(const e of i){const i=o(e,t);if(i)return i}}else{let i=r(e,t);for(;i;){const e=o(i,t);if(e)return e;i=d(i,t)}}return!t&&h(e)?e:null}function r(e,t){return t?e.firstElementChild:e.lastElementChild}function d(e,t){return t?e.nextElementSibling:e.previousElementSibling}const h=e=>!e.shadowRoot?.delegatesFocus&&(e.matches(s.join(","))&&!(e=>!(!e.matches("details:not([open]) *")||e.matches("details>summary:first-of-type"))||!(e.offsetWidth||e.offsetHeight||e.getClientRects().length))(e));function l(e=document){const t=e.activeElement;return t?t.shadowRoot?l(t.shadowRoot)||document.activeElement:t:null}function a(e,t){const[i,s]=function(e){const t=o(e,!0);return[t,t?o(e,!1)||t:null]}(e);if(!i)return t.preventDefault();const n=l();t.shiftKey&&n===i?(s.focus(),t.preventDefault()):t.shiftKey||n!==s||(i.focus(),t.preventDefault())}function u(e,t){return function t(i){if(!i||i===document||i===window)return null;const s=c(i);return s&&(i=s),i.closest(e)||t(i.getRootNode().host)}(t)}function c(e){return e.assignedSlot||(e.parentNode?c(e.parentNode):null)}const f="data-a11y-dialog";class ${$el;id;previouslyFocused;shown;constructor(e){this.$el=e,this.id=this.$el.getAttribute(f)||this.$el.id,this.previouslyFocused=null,this.shown=!1,this.maintainFocus=this.maintainFocus.bind(this),this.bindKeypress=this.bindKeypress.bind(this),this.handleTriggerClicks=this.handleTriggerClicks.bind(this),this.show=this.show.bind(this),this.hide=this.hide.bind(this),this.$el.setAttribute("aria-hidden","true"),this.$el.setAttribute("aria-modal","true"),this.$el.setAttribute("tabindex","-1"),this.$el.hasAttribute("role")||this.$el.setAttribute("role","dialog"),document.addEventListener("click",this.handleTriggerClicks,!0)}destroy(){return this.fire("destroy").defaultPrevented||(this.hide(),document.removeEventListener("click",this.handleTriggerClicks,!0),this.$el.replaceWith(this.$el.cloneNode(!0))),this}show(e){if(this.shown)return this;return this.fire("show",e).defaultPrevented||(this.shown=!0,this.$el.removeAttribute("aria-hidden"),this.previouslyFocused=l(),"BODY"===this.previouslyFocused?.tagName&&e?.target&&(this.previouslyFocused=e.target),"focus"===e?.type?this.maintainFocus():n(this.$el),document.body.addEventListener("focus",this.maintainFocus,!0),this.$el.addEventListener("keydown",this.bindKeypress,!0)),this}hide(e){if(!this.shown)return this;return this.fire("hide",e).defaultPrevented||(this.shown=!1,this.$el.setAttribute("aria-hidden","true"),document.body.removeEventListener("focus",this.maintainFocus,!0),this.$el.removeEventListener("keydown",this.bindKeypress,!0),this.previouslyFocused?.focus?.()),this}on(e,t,i){return this.$el.addEventListener(e,t,i),this}off(e,t,i){return this.$el.removeEventListener(e,t,i),this}fire(e,t){const i=new CustomEvent(e,{detail:t,cancelable:!0});return this.$el.dispatchEvent(i),i}handleTriggerClicks(e){const t=e.composedPath()[0],i=u(`[${f}-show="${this.id}"]`,t),s=u(`[${f}-hide="${this.id}"]`,t),n=u(`[${f}-hide]`,t)&&u('[aria-modal="true"]',t)===this.$el;i&&this.show(e),(s||n)&&this.hide(e)}bindKeypress(e){if(u('[aria-modal="true"]',l())!==this.$el)return;let t=!1;try{t=!!this.$el.querySelector('[popover]:not([popover="manual"]):popover-open')}catch{}"Escape"!==e.key||"alertdialog"===this.$el.getAttribute("role")||t||(e.preventDefault(),this.hide(e)),"Tab"===e.key&&a(this.$el,e)}maintainFocus(){const e=l();u(`[aria-modal="true"], [${f}-ignore-focus-trap]`,e)||n(this.$el)}}function m(){for(const e of document.querySelectorAll("[data-a11y-dialog]"))new $(e)}return"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",m):m()),$}));