123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 |
- import { hasContentVirtual } from '../commons/dom';
- import { getExplicitRole } from '../commons/aria';
- import standards from '../standards';
- import {
- querySelectorAll,
- getScroll,
- closest,
- getRootNode,
- tokenList
- } from '../core/utils';
- function scrollableRegionFocusableMatches(node, virtualNode) {
- /**
- * Note:
- * `excludeHidden=true` for this rule, thus considering only elements in the accessibility tree.
- */
- /**
- * if not scrollable -> `return`
- */
- if (!!getScroll(node, 13) === false) {
- return false;
- }
- /**
- * ignore scrollable regions owned by combobox. limit to roles
- * ownable by combobox so we don't keep calling closest for every
- * node (which would be slow)
- * @see https://github.com/dequelabs/axe-core/issues/1763
- */
- const role = getExplicitRole(virtualNode);
- if (standards.ariaRoles.combobox.requiredOwned.includes(role)) {
- // in ARIA 1.1 the container has role=combobox
- if (closest(virtualNode, '[role~="combobox"]')) {
- return false;
- }
- // in ARIA 1.0 and 1.2 the combobox owns (1.0) or controls (1.2)
- // the listbox
- const id = virtualNode.attr('id');
- if (id) {
- const doc = getRootNode(node);
- const owned = Array.from(
- doc.querySelectorAll(`[aria-owns~="${id}"], [aria-controls~="${id}"]`)
- );
- const comboboxOwned = owned.some(el => {
- const roles = tokenList(el.getAttribute('role'));
- return roles.includes('combobox');
- });
- if (comboboxOwned) {
- return false;
- }
- }
- }
- /**
- * check if node has visible contents
- */
- const nodeAndDescendents = querySelectorAll(virtualNode, '*');
- const hasVisibleChildren = nodeAndDescendents.some(elm =>
- hasContentVirtual(
- elm,
- true, // noRecursion
- true // ignoreAria
- )
- );
- if (!hasVisibleChildren) {
- return false;
- }
- return true;
- }
- export default scrollableRegionFocusableMatches;
|