aria-api

access ARIA information from JavaScript
git clone https://git.ce9e.org/aria-api.git

commit
50c2d3fcb29dc090987b6307c2708a86fb288a37
parent
f2d0aeb3c33b18dd77b9d66160154997687a7180
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2026-02-18 21:33
shadow-dom: use getRootNode() instead of document

This also means we can longer globally query for owners.

From my testing the performance impact is not too bad.

Diffstat

M lib/atree.js 43 +++++++++++++++++--------------------------
M lib/name.js 4 ++--

2 files changed, 19 insertions, 28 deletions


diff --git a/lib/atree.js b/lib/atree.js

@@ -1,27 +1,19 @@
    1     1 import * as attrs from './attrs';
    2     2 
    3    -1 const _getOwner = function(node, owners) {
   -1     3 const _getOwner = function(node) {
    4     4 	if (node.nodeType === node.ELEMENT_NODE && node.id) {
    5    -1 		const selector = '[aria-owns~="' + CSS.escape(node.id) + '"]';
    6    -1 		if (owners) {
    7    -1 			for (const owner of owners) {
    8    -1 				if (owner.matches(selector)) {
    9    -1 					return owner;
   10    -1 				}
   11    -1 			}
   12    -1 		} else {
   13    -1 			return document.querySelector(selector);
   14    -1 		}
   -1     5 		const selector = `[aria-owns~="${CSS.escape(node.id)}"]`;
   -1     6 		return node.getRootNode().querySelector(selector);
   15     7 	}
   16     8 };
   17     9 
   18    -1 const _getParentNode = function(node, owners) {
   19    -1 	return _getOwner(node, owners) || node.parentNode;
   -1    10 const _getParentNode = function(node) {
   -1    11 	return _getOwner(node) || node.parentNode;
   20    12 };
   21    13 
   22    -1 const detectLoop = function(node, owners) {
   -1    14 const detectLoop = function(node) {
   23    15 	const seen = [node];
   24    -1 	while ((node = _getParentNode(node, owners))) {
   -1    16 	while ((node = _getParentNode(node))) {
   25    17 		if (seen.includes(node)) {
   26    18 			return true;
   27    19 		}
@@ -29,27 +21,27 @@ const detectLoop = function(node, owners) {
   29    21 	}
   30    22 };
   31    23 
   32    -1 const getOwner = function(node, owners) {
   33    -1 	const owner = _getOwner(node, owners);
   34    -1 	if (owner && !detectLoop(node, owners)) {
   -1    24 const getOwner = function(node) {
   -1    25 	const owner = _getOwner(node);
   -1    26 	if (owner && !detectLoop(node)) {
   35    27 		return owner;
   36    28 	}
   37    29 };
   38    30 
   39    -1 export const getParentNode = function(node, owners) {
   40    -1 	return getOwner(node, owners) || node.parentNode;
   -1    31 export const getParentNode = function(node) {
   -1    32 	return getOwner(node) || node.parentNode;
   41    33 };
   42    34 
   43    35 const isHidden = function(node) {
   44    36 	return node.nodeType === node.ELEMENT_NODE && attrs.getAttribute(node, 'hidden');
   45    37 };
   46    38 
   47    -1 export const getChildNodes = function(node, owners) {
   -1    39 export const getChildNodes = function(node) {
   48    40 	const childNodes = [];
   49    41 
   50    42 	for (let i = 0; i < node.childNodes.length; i++) {
   51    43 		const child = node.childNodes[i];
   52    -1 		if (!getOwner(child, owners) && !isHidden(child)) {
   -1    44 		if (!getOwner(child) && !isHidden(child)) {
   53    45 			childNodes.push(child);
   54    46 		}
   55    47 	}
@@ -57,9 +49,9 @@ export const getChildNodes = function(node, owners) {
   57    49 	if (node.nodeType === node.ELEMENT_NODE) {
   58    50 		const owns = attrs.getAttribute(node, 'owns') || [];
   59    51 		for (let i = 0; i < owns.length; i++) {
   60    -1 			const child = document.getElementById(owns[i]);
   -1    52 			const child = node.getRootNode().getElementById(owns[i]);
   61    53 			// double check with getOwner for consistency
   62    -1 			if (child && getOwner(child, owners) === node && !isHidden(child)) {
   -1    54 			if (child && getOwner(child) === node && !isHidden(child)) {
   63    55 				childNodes.push(child);
   64    56 			}
   65    57 		}
@@ -69,12 +61,11 @@ export const getChildNodes = function(node, owners) {
   69    61 };
   70    62 
   71    63 export const walk = function(root, fn) {
   72    -1 	const owners = document.querySelectorAll('[aria-owns]');
   73    64 	let queue = [root];
   74    65 	while (queue.length) {
   75    66 		const item = queue.shift();
   76    67 		fn(item);
   77    -1 		queue = getChildNodes(item, owners).concat(queue);
   -1    68 		queue = getChildNodes(item).concat(queue);
   78    69 	}
   79    70 };
   80    71 

diff --git a/lib/name.js b/lib/name.js

@@ -94,7 +94,7 @@ const getNameRaw = function(el, recursive, ongoingLabelledBy, visited, directRef
   94    94 	if (!ongoingLabelledBy && el.matches('[aria-labelledby]')) {
   95    95 		const ids = el.getAttribute('aria-labelledby').split(/\s+/);
   96    96 		const strings = ids.map(id => {
   97    -1 			const label = document.getElementById(id);
   -1    97 			const label = el.getRootNode().getElementById(id);
   98    98 			return label ? getNameRaw(label, true, true, visited, true) : '';
   99    99 		});
  100   100 		ret = strings.join(' ');
@@ -205,7 +205,7 @@ export const getDescription = function(el) {
  205   205 	if (el.matches('[aria-describedby]')) {
  206   206 		const ids = el.getAttribute('aria-describedby').split(/\s+/);
  207   207 		const strings = ids.map(id => {
  208    -1 			const label = document.getElementById(id);
   -1   208 			const label = el.getRootNode().getElementById(id);
  209   209 			return label ? getNameRaw(label, true, true) : '';
  210   210 		});
  211   211 		ret = strings.join(' ');