123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- import * as dom from '../../commons/dom';
- import * as aria from '../../commons/aria';
- import * as standards from '../../commons/standards';
- import matches from '../../commons/matches';
- import cache from '../../core/base/cache';
- const landmarkRoles = standards.getAriaRolesByType('landmark');
- const implicitAriaLiveRoles = ['alert', 'log', 'status'];
- // Check if the current element is a landmark
- function isRegion(virtualNode, options) {
- const node = virtualNode.actualNode;
- const role = aria.getRole(virtualNode);
- const ariaLive = (node.getAttribute('aria-live') || '').toLowerCase().trim();
- // Ignore content inside of aria-live
- if (
- ['assertive', 'polite'].includes(ariaLive) ||
- implicitAriaLiveRoles.includes(role)
- ) {
- return true;
- }
- // Check if the node matches a landmark role
- if (landmarkRoles.includes(role)) {
- return true;
- }
- // Check if node matches an option
- if (options.regionMatcher && matches(virtualNode, options.regionMatcher)) {
- return true;
- }
- return false;
- }
- /**
- * Find all visible elements not wrapped inside a landmark or skiplink
- */
- function findRegionlessElms(virtualNode, options) {
- const node = virtualNode.actualNode;
- // End recursion if the element is a landmark, skiplink, or hidden content
- if (
- isRegion(virtualNode, options) ||
- (dom.isSkipLink(virtualNode.actualNode) &&
- dom.getElementByReference(virtualNode.actualNode, 'href')) ||
- !dom.isVisible(node, true)
- ) {
- // Mark each parent node as having region descendant
- let vNode = virtualNode;
- while (vNode) {
- vNode._hasRegionDescendant = true;
- vNode = vNode.parent;
- }
- return [];
- // Return the node is a content element. Ignore any direct text children
- // of body so we don't report body as being outside of a landmark.
- // @see https://github.com/dequelabs/axe-core/issues/2049
- } else if (
- node !== document.body &&
- dom.hasContent(node, /* noRecursion: */ true)
- ) {
- return [virtualNode];
- // Recursively look at all child elements
- } else {
- return virtualNode.children
- .filter(({ actualNode }) => actualNode.nodeType === 1)
- .map(vNode => findRegionlessElms(vNode, options))
- .reduce((a, b) => a.concat(b), []); // flatten the results
- }
- }
- function regionEvaluate(node, options, virtualNode) {
- let regionlessNodes = cache.get('regionlessNodes');
- if (regionlessNodes) {
- return !regionlessNodes.includes(virtualNode);
- }
- const tree = axe._tree;
- regionlessNodes = findRegionlessElms(tree[0], options)
- // Find first parent marked as having region descendant (or body) and
- // return the node right before it as the "outer" element
- .map(vNode => {
- while (
- vNode.parent &&
- !vNode.parent._hasRegionDescendant &&
- vNode.parent.actualNode !== document.body
- ) {
- vNode = vNode.parent;
- }
- return vNode;
- })
- // Remove duplicate containers
- .filter((vNode, index, array) => {
- return array.indexOf(vNode) === index;
- });
- cache.set('regionlessNodes', regionlessNodes);
- return !regionlessNodes.includes(virtualNode);
- }
- export default regionEvaluate;
|