aria.mjs 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. //#region ../../packages/utils/dom/aria.ts
  2. const FOCUSABLE_ELEMENT_SELECTORS = `a[href],button:not([disabled]),button:not([hidden]),:not([tabindex="-1"]),input:not([disabled]),input:not([type="hidden"]),select:not([disabled]),textarea:not([disabled])`;
  3. const isShadowRoot = (e) => {
  4. if (typeof ShadowRoot === "undefined") return false;
  5. return e instanceof ShadowRoot;
  6. };
  7. const isHTMLElement = (e) => {
  8. if (typeof Element === "undefined") return false;
  9. return e instanceof Element;
  10. };
  11. /**
  12. * Determine if the testing element is visible on screen no matter if its on the viewport or not
  13. */
  14. const isVisible = (element) => {
  15. return getComputedStyle(element).position === "fixed" ? false : element.offsetParent !== null;
  16. };
  17. const obtainAllFocusableElements = (element) => {
  18. return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENT_SELECTORS)).filter((item) => isFocusable(item) && isVisible(item));
  19. };
  20. /**
  21. * @desc Determine if target element is focusable
  22. * @param element {HTMLElement}
  23. * @returns {Boolean} true if it is focusable
  24. */
  25. const isFocusable = (element) => {
  26. if (element.tabIndex > 0 || element.tabIndex === 0 && element.getAttribute("tabIndex") !== null) return true;
  27. if (element.tabIndex < 0 || element.hasAttribute("disabled") || element.getAttribute("aria-disabled") === "true") return false;
  28. switch (element.nodeName) {
  29. case "A": return !!element.href && element.rel !== "ignore";
  30. case "INPUT": return !(element.type === "hidden" || element.type === "file");
  31. case "BUTTON":
  32. case "SELECT":
  33. case "TEXTAREA": return true;
  34. default: return false;
  35. }
  36. };
  37. /**
  38. * Trigger an event
  39. * mouseenter, mouseleave, mouseover, keyup, change, click, etc.
  40. * @param {HTMLElement} elm
  41. * @param {String} name
  42. * @param {*} opts
  43. */
  44. const triggerEvent = function(elm, name, ...opts) {
  45. let eventName;
  46. if (name.includes("mouse") || name.includes("click")) eventName = "MouseEvents";
  47. else if (name.includes("key")) eventName = "KeyboardEvent";
  48. else eventName = "HTMLEvents";
  49. const evt = document.createEvent(eventName);
  50. evt.initEvent(name, ...opts);
  51. elm.dispatchEvent(evt);
  52. return elm;
  53. };
  54. const isLeaf = (el) => !el.getAttribute("aria-owns");
  55. const getSibling = (el, distance, elClass) => {
  56. const { parentNode } = el;
  57. if (!parentNode) return null;
  58. const siblings = parentNode.querySelectorAll(elClass);
  59. return siblings[Array.prototype.indexOf.call(siblings, el) + distance] || null;
  60. };
  61. const focusElement = (el, options) => {
  62. if (!el || !el.focus) return;
  63. let cleanup = false;
  64. if (isHTMLElement(el) && !isFocusable(el) && !el.getAttribute("tabindex")) {
  65. el.setAttribute("tabindex", "-1");
  66. cleanup = true;
  67. }
  68. el.focus(options);
  69. if (isHTMLElement(el) && cleanup) el.removeAttribute("tabindex");
  70. };
  71. const focusNode = (el) => {
  72. if (!el) return;
  73. focusElement(el);
  74. !isLeaf(el) && el.click();
  75. };
  76. //#endregion
  77. export { focusElement, focusNode, getSibling, isFocusable, isLeaf, isShadowRoot, isVisible, obtainAllFocusableElements, triggerEvent };
  78. //# sourceMappingURL=aria.mjs.map