Skip to content

Instantly share code, notes, and snippets.

@NickHeiner
Created November 6, 2024 19:21
Show Gist options
  • Save NickHeiner/a84700177a3d6c80f428ce985f08a217 to your computer and use it in GitHub Desktop.
Save NickHeiner/a84700177a3d6c80f428ce985f08a217 to your computer and use it in GitHub Desktop.
export interface AccessibilityNode {
tag: string;
role: string;
name: string;
children: AccessibilityNode[];
}
/**
* Once AOM support is broadly available, we should use that instead of this.
*
* This also produces a lot of intermediate nodes, which I'm not sure are
* desirable. It seems like maybe we only want the leaves.
*
* For example: { "tag": "div", "role": "div", "name": "refresh the page.",
* "children": [ { "tag": "p", "role": "p", "name": "refresh the page.",
* "children": [ { "tag": "a", "role": "a", "name": "refresh the page.",
* "children": [] } ] } ] }
*
* Or: { "tag": "div", "role": "div", "name": "loading\n reading\n writing\n
* saving\n searching\n \n \n refresh the page.", "children": [ { "tag": "div",
* "role": "div", "name": "", "children": [] }, { "tag": "div", "role": "div",
* "name": "loading", "children": [] }, { "tag": "div", "role": "div", "name":
* "reading", "children": [] },
*/
export function getAccessibilityTree(html: string) {
function getA11yInfo(node: HTMLElement): AccessibilityNode | null {
// Skip hidden elements
if (
node.nodeType !== Node.ELEMENT_NODE ||
node.hasAttribute("aria-hidden") ||
node.style.display === "none"
) {
return null;
}
// Collect basic accessibility information
const role = node.getAttribute("role") || node.tagName.toLowerCase();
const name = node.getAttribute("aria-label") || node.textContent?.trim();
const a11yNode: AccessibilityNode = {
tag: node.tagName.toLowerCase(),
role,
name: name || "",
children: [],
};
// Recursively gather children info
node.childNodes.forEach((child) => {
const childA11yInfo = getA11yInfo(
// TODO is this safe?
child as HTMLElement,
);
if (childA11yInfo) {
a11yNode.children.push(childA11yInfo);
}
});
return a11yNode;
}
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
const root = doc.body;
return getA11yInfo(root);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment