getRole.mjs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // https://w3c.github.io/html-aria/#document-conformance-requirements-for-use-of-aria-attributes-in-html
  2. import { getLocalName } from "./util.mjs";
  3. var localNameToRoleMappings = {
  4. article: "article",
  5. aside: "complementary",
  6. body: "document",
  7. button: "button",
  8. datalist: "listbox",
  9. dd: "definition",
  10. details: "group",
  11. dialog: "dialog",
  12. dt: "term",
  13. fieldset: "group",
  14. figure: "figure",
  15. // WARNING: Only with an accessible name
  16. form: "form",
  17. footer: "contentinfo",
  18. h1: "heading",
  19. h2: "heading",
  20. h3: "heading",
  21. h4: "heading",
  22. h5: "heading",
  23. h6: "heading",
  24. header: "banner",
  25. hr: "separator",
  26. legend: "legend",
  27. li: "listitem",
  28. math: "math",
  29. main: "main",
  30. menu: "list",
  31. nav: "navigation",
  32. ol: "list",
  33. optgroup: "group",
  34. // WARNING: Only in certain context
  35. option: "option",
  36. output: "status",
  37. progress: "progressbar",
  38. // WARNING: Only with an accessible name
  39. section: "region",
  40. summary: "button",
  41. table: "table",
  42. tbody: "rowgroup",
  43. textarea: "textbox",
  44. tfoot: "rowgroup",
  45. // WARNING: Only in certain context
  46. td: "cell",
  47. th: "columnheader",
  48. thead: "rowgroup",
  49. tr: "row",
  50. ul: "list"
  51. };
  52. var prohibitedAttributes = {
  53. caption: new Set(["aria-label", "aria-labelledby"]),
  54. code: new Set(["aria-label", "aria-labelledby"]),
  55. deletion: new Set(["aria-label", "aria-labelledby"]),
  56. emphasis: new Set(["aria-label", "aria-labelledby"]),
  57. generic: new Set(["aria-label", "aria-labelledby", "aria-roledescription"]),
  58. insertion: new Set(["aria-label", "aria-labelledby"]),
  59. paragraph: new Set(["aria-label", "aria-labelledby"]),
  60. presentation: new Set(["aria-label", "aria-labelledby"]),
  61. strong: new Set(["aria-label", "aria-labelledby"]),
  62. subscript: new Set(["aria-label", "aria-labelledby"]),
  63. superscript: new Set(["aria-label", "aria-labelledby"])
  64. };
  65. /**
  66. *
  67. * @param element
  68. * @param role The role used for this element. This is specified to control whether you want to use the implicit or explicit role.
  69. */
  70. function hasGlobalAriaAttributes(element, role) {
  71. // https://rawgit.com/w3c/aria/stable/#global_states
  72. // commented attributes are deprecated
  73. return ["aria-atomic", "aria-busy", "aria-controls", "aria-current", "aria-describedby", "aria-details", // "disabled",
  74. "aria-dropeffect", // "errormessage",
  75. "aria-flowto", "aria-grabbed", // "haspopup",
  76. "aria-hidden", // "invalid",
  77. "aria-keyshortcuts", "aria-label", "aria-labelledby", "aria-live", "aria-owns", "aria-relevant", "aria-roledescription"].some(function (attributeName) {
  78. var _prohibitedAttributes;
  79. return element.hasAttribute(attributeName) && !((_prohibitedAttributes = prohibitedAttributes[role]) !== null && _prohibitedAttributes !== void 0 && _prohibitedAttributes.has(attributeName));
  80. });
  81. }
  82. function ignorePresentationalRole(element, implicitRole) {
  83. // https://rawgit.com/w3c/aria/stable/#conflict_resolution_presentation_none
  84. return hasGlobalAriaAttributes(element, implicitRole);
  85. }
  86. export default function getRole(element) {
  87. var explicitRole = getExplicitRole(element);
  88. if (explicitRole === null || explicitRole === "presentation") {
  89. var implicitRole = getImplicitRole(element);
  90. if (explicitRole !== "presentation" || ignorePresentationalRole(element, implicitRole || "")) {
  91. return implicitRole;
  92. }
  93. }
  94. return explicitRole;
  95. }
  96. function getImplicitRole(element) {
  97. var mappedByTag = localNameToRoleMappings[getLocalName(element)];
  98. if (mappedByTag !== undefined) {
  99. return mappedByTag;
  100. }
  101. switch (getLocalName(element)) {
  102. case "a":
  103. case "area":
  104. case "link":
  105. if (element.hasAttribute("href")) {
  106. return "link";
  107. }
  108. break;
  109. case "img":
  110. if (element.getAttribute("alt") === "" && !ignorePresentationalRole(element, "img")) {
  111. return "presentation";
  112. }
  113. return "img";
  114. case "input":
  115. {
  116. var _ref = element,
  117. type = _ref.type;
  118. switch (type) {
  119. case "button":
  120. case "image":
  121. case "reset":
  122. case "submit":
  123. return "button";
  124. case "checkbox":
  125. case "radio":
  126. return type;
  127. case "range":
  128. return "slider";
  129. case "email":
  130. case "tel":
  131. case "text":
  132. case "url":
  133. if (element.hasAttribute("list")) {
  134. return "combobox";
  135. }
  136. return "textbox";
  137. case "search":
  138. if (element.hasAttribute("list")) {
  139. return "combobox";
  140. }
  141. return "searchbox";
  142. default:
  143. return null;
  144. }
  145. }
  146. case "select":
  147. if (element.hasAttribute("multiple") || element.size > 1) {
  148. return "listbox";
  149. }
  150. return "combobox";
  151. }
  152. return null;
  153. }
  154. function getExplicitRole(element) {
  155. var role = element.getAttribute("role");
  156. if (role !== null) {
  157. var explicitRole = role.trim().split(" ")[0]; // String.prototype.split(sep, limit) will always return an array with at least one member
  158. // as long as limit is either undefined or > 0
  159. if (explicitRole.length > 0) {
  160. return explicitRole;
  161. }
  162. }
  163. return null;
  164. }
  165. //# sourceMappingURL=getRole.mjs.map