Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@krautzource/aria-tree-walker

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@krautzource/aria-tree-walker - npm Package Compare versions

Comparing version 2.0.0 to 3.0.0

9

CHANGELOG.md

@@ -5,2 +5,11 @@ # Changelog

## [3.0.0](https://github.com/krautzource/aria-tree-walker/compare/v2.0.0...v3.0.0) (2021-03-21)
### ⚠ BREAKING CHANGES
* No longer supports aria-owns+id attributes for tree structure and switches focus management to roving tabindex.
* !feat: Revise data structure and focus management. ([da1f0b6](https://github.com/krautzource/aria-tree-walker/commit/da1f0b6723b78ae731603b773a628255a6066738)), closes [#25](https://github.com/krautzource/aria-tree-walker/issues/25)
## [2.0.0](https://github.com/krautzource/aria-tree-walker/compare/v1.0.0...v2.0.0) (2021-02-27)

@@ -7,0 +16,0 @@

2

dist/aria-tree-walker.cjs.js

@@ -1,1 +0,1 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const t=i=>{const s=new e(i.id);return"A"===i.tagName.toUpperCase()&&""!==i.getAttribute("href")&&(s.href=i.getAttribute("href"),i.setAttribute("tabindex","-1")),i.getAttribute("aria-owns")?(i.getAttribute("aria-owns").split(" ").forEach((e=>{const i=document.getElementById(e),a=t(i);a.parent=s,s.children.push(a)})),s):s};class e{constructor(t){this.name=t,this.parent=null,this.href=null,this.children=[]}}class i{constructor(t){this.root=t,this.active=t}up(){this.active.parent&&(this.active=this.active.parent)}down(){this.active.children.length&&(this.active=this.active.children[0])}left(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t>0&&(this.active=this.active.parent.children[t-1])}}right(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t<this.active.parent.children.length-1&&(this.active=this.active.parent.children[t+1])}}activateLink(){this.active.href&&(window.location.href=this.active.href)}}class s{constructor(e){this.node=e,this.tree=(e=>{const s=e.hasAttribute("aria-owns")?e:e.querySelector("[aria-owns]");return s?new i(t(s)):console.warn("no aria-owns attribut in tree:",e)})(e),this.node.addEventListener("keydown",this.move.bind(this)),this.node.addEventListener("focusin",this.highlight.bind(this,!0)),this.node.addEventListener("focusout",this.highlight.bind(this,!1))}active(){return this.tree.active}move(t){switch(this.highlight(!1),[32,37,38,39,40,13,32].indexOf(t.keyCode)>-1&&t.preventDefault(),t.keyCode){case 37:this.tree.left();break;case 38:this.tree.up();break;case 39:this.tree.right();break;case 40:this.tree.down();break;case 32:case 13:this.tree.activateLink()}this.highlight(!0),this.node.setAttribute("aria-activedescendant",this.active().name)}highlight(t){const e=this.active().name===this.node.id?this.node:this.node.querySelector("#"+this.active().name);!0===t&&e.classList.add("is-activedescendant"),!1===t&&e.classList.remove("is-activedescendant")}}exports.attachNavigator=function(t){new s(t),t.setAttribute("tabindex","0")};
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const t=i=>{const s=new e(i.getAttribute("data-owns-id"));"A"===i.tagName.toUpperCase()&&""!==i.getAttribute("href")&&(s.href=i.getAttribute("href"),i.setAttribute("tabindex","-1"));const a=i.getAttribute("data-owns");return a?(a.split(" ").forEach((e=>{const i=document.querySelector(`[data-owns-id="${e}"]`),a=t(i);a.parent=s,s.children.push(a)})),s):s};class e{constructor(t){this.name=t,this.parent=null,this.href=null,this.children=[]}}class i{constructor(t){this.root=t,this.active=t}up(){this.active.parent&&(this.active=this.active.parent)}down(){this.active.children.length&&(this.active=this.active.children[0])}left(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t>0&&(this.active=this.active.parent.children[t-1])}}right(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t<this.active.parent.children.length-1&&(this.active=this.active.parent.children[t+1])}}activateLink(){this.active.href&&(window.location.href=this.active.href)}}class s{constructor(e){this.node=e,this.tree=(e=>{const s=e.hasAttribute("data-owns")?e:e.querySelector("[data-owns]");return s?new i(t(s)):console.warn("no data-owns attribute in tree:",e)})(e),this.node.addEventListener("keydown",this.move.bind(this)),this.node.addEventListener("focusin",(()=>{this.node.setAttribute("tabindex","-1"),this.highlight(!0)})),this.node.addEventListener("focusout",(()=>{this.highlight(!1),this.node.setAttribute("tabindex","0")}))}active(){return this.tree.active}move(t){switch(this.highlight(!1),[32,37,38,39,40,13,32].indexOf(t.keyCode)>-1&&t.preventDefault(),t.keyCode){case 37:this.tree.left();break;case 38:this.tree.up();break;case 39:this.tree.right();break;case 40:this.tree.down();break;case 32:case 13:this.tree.activateLink()}this.highlight(!0)}highlight(t){const e=this.active().name===this.node.getAttribute("data-owns-id")?this.node:this.node.querySelector(`[data-owns-id="${this.active().name}"]`);!0===t&&(e.setAttribute("tabindex","0"),e.classList.add("is-activedescendant"),e.focus()),!1===t&&(e.setAttribute("tabindex","-1"),e.classList.remove("is-activedescendant"))}}exports.attachNavigator=function(t){new s(t),t.setAttribute("tabindex","0")};

@@ -1,1 +0,1 @@

const t=i=>{const s=new e(i.id);return"A"===i.tagName.toUpperCase()&&""!==i.getAttribute("href")&&(s.href=i.getAttribute("href"),i.setAttribute("tabindex","-1")),i.getAttribute("aria-owns")?(i.getAttribute("aria-owns").split(" ").forEach((e=>{const i=document.getElementById(e),a=t(i);a.parent=s,s.children.push(a)})),s):s};class e{constructor(t){this.name=t,this.parent=null,this.href=null,this.children=[]}}class i{constructor(t){this.root=t,this.active=t}up(){this.active.parent&&(this.active=this.active.parent)}down(){this.active.children.length&&(this.active=this.active.children[0])}left(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t>0&&(this.active=this.active.parent.children[t-1])}}right(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t<this.active.parent.children.length-1&&(this.active=this.active.parent.children[t+1])}}activateLink(){this.active.href&&(window.location.href=this.active.href)}}function s(t){new a(t),t.setAttribute("tabindex","0")}class a{constructor(e){this.node=e,this.tree=(e=>{const s=e.hasAttribute("aria-owns")?e:e.querySelector("[aria-owns]");return s?new i(t(s)):console.warn("no aria-owns attribut in tree:",e)})(e),this.node.addEventListener("keydown",this.move.bind(this)),this.node.addEventListener("focusin",this.highlight.bind(this,!0)),this.node.addEventListener("focusout",this.highlight.bind(this,!1))}active(){return this.tree.active}move(t){switch(this.highlight(!1),[32,37,38,39,40,13,32].indexOf(t.keyCode)>-1&&t.preventDefault(),t.keyCode){case 37:this.tree.left();break;case 38:this.tree.up();break;case 39:this.tree.right();break;case 40:this.tree.down();break;case 32:case 13:this.tree.activateLink()}this.highlight(!0),this.node.setAttribute("aria-activedescendant",this.active().name)}highlight(t){const e=this.active().name===this.node.id?this.node:this.node.querySelector("#"+this.active().name);!0===t&&e.classList.add("is-activedescendant"),!1===t&&e.classList.remove("is-activedescendant")}}export{s as attachNavigator};
const t=i=>{const s=new e(i.getAttribute("data-owns-id"));"A"===i.tagName.toUpperCase()&&""!==i.getAttribute("href")&&(s.href=i.getAttribute("href"),i.setAttribute("tabindex","-1"));const a=i.getAttribute("data-owns");return a?(a.split(" ").forEach((e=>{const i=document.querySelector(`[data-owns-id="${e}"]`),a=t(i);a.parent=s,s.children.push(a)})),s):s};class e{constructor(t){this.name=t,this.parent=null,this.href=null,this.children=[]}}class i{constructor(t){this.root=t,this.active=t}up(){this.active.parent&&(this.active=this.active.parent)}down(){this.active.children.length&&(this.active=this.active.children[0])}left(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t>0&&(this.active=this.active.parent.children[t-1])}}right(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t<this.active.parent.children.length-1&&(this.active=this.active.parent.children[t+1])}}activateLink(){this.active.href&&(window.location.href=this.active.href)}}function s(t){new a(t),t.setAttribute("tabindex","0")}class a{constructor(e){this.node=e,this.tree=(e=>{const s=e.hasAttribute("data-owns")?e:e.querySelector("[data-owns]");return s?new i(t(s)):console.warn("no data-owns attribute in tree:",e)})(e),this.node.addEventListener("keydown",this.move.bind(this)),this.node.addEventListener("focusin",(()=>{this.node.setAttribute("tabindex","-1"),this.highlight(!0)})),this.node.addEventListener("focusout",(()=>{this.highlight(!1),this.node.setAttribute("tabindex","0")}))}active(){return this.tree.active}move(t){switch(this.highlight(!1),[32,37,38,39,40,13,32].indexOf(t.keyCode)>-1&&t.preventDefault(),t.keyCode){case 37:this.tree.left();break;case 38:this.tree.up();break;case 39:this.tree.right();break;case 40:this.tree.down();break;case 32:case 13:this.tree.activateLink()}this.highlight(!0)}highlight(t){const e=this.active().name===this.node.getAttribute("data-owns-id")?this.node:this.node.querySelector(`[data-owns-id="${this.active().name}"]`);!0===t&&(e.setAttribute("tabindex","0"),e.classList.add("is-activedescendant"),e.focus()),!1===t&&(e.setAttribute("tabindex","-1"),e.classList.remove("is-activedescendant"))}}export{s as attachNavigator};

@@ -1,1 +0,1 @@

var AriaTreeWalker=function(t){"use strict";const e=t=>{const s=new i(t.id);return"A"===t.tagName.toUpperCase()&&""!==t.getAttribute("href")&&(s.href=t.getAttribute("href"),t.setAttribute("tabindex","-1")),t.getAttribute("aria-owns")?(t.getAttribute("aria-owns").split(" ").forEach((t=>{const i=document.getElementById(t),a=e(i);a.parent=s,s.children.push(a)})),s):s};class i{constructor(t){this.name=t,this.parent=null,this.href=null,this.children=[]}}class s{constructor(t){this.root=t,this.active=t}up(){this.active.parent&&(this.active=this.active.parent)}down(){this.active.children.length&&(this.active=this.active.children[0])}left(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t>0&&(this.active=this.active.parent.children[t-1])}}right(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t<this.active.parent.children.length-1&&(this.active=this.active.parent.children[t+1])}}activateLink(){this.active.href&&(window.location.href=this.active.href)}}class a{constructor(t){this.node=t,this.tree=(t=>{const i=t.hasAttribute("aria-owns")?t:t.querySelector("[aria-owns]");return i?new s(e(i)):console.warn("no aria-owns attribut in tree:",t)})(t),this.node.addEventListener("keydown",this.move.bind(this)),this.node.addEventListener("focusin",this.highlight.bind(this,!0)),this.node.addEventListener("focusout",this.highlight.bind(this,!1))}active(){return this.tree.active}move(t){switch(this.highlight(!1),[32,37,38,39,40,13,32].indexOf(t.keyCode)>-1&&t.preventDefault(),t.keyCode){case 37:this.tree.left();break;case 38:this.tree.up();break;case 39:this.tree.right();break;case 40:this.tree.down();break;case 32:case 13:this.tree.activateLink()}this.highlight(!0),this.node.setAttribute("aria-activedescendant",this.active().name)}highlight(t){const e=this.active().name===this.node.id?this.node:this.node.querySelector("#"+this.active().name);!0===t&&e.classList.add("is-activedescendant"),!1===t&&e.classList.remove("is-activedescendant")}}return t.attachNavigator=function(t){new a(t),t.setAttribute("tabindex","0")},Object.defineProperty(t,"__esModule",{value:!0}),t}({});
var AriaTreeWalker=function(t){"use strict";const e=t=>{const s=new i(t.getAttribute("data-owns-id"));"A"===t.tagName.toUpperCase()&&""!==t.getAttribute("href")&&(s.href=t.getAttribute("href"),t.setAttribute("tabindex","-1"));const a=t.getAttribute("data-owns");return a?(a.split(" ").forEach((t=>{const i=document.querySelector(`[data-owns-id="${t}"]`),a=e(i);a.parent=s,s.children.push(a)})),s):s};class i{constructor(t){this.name=t,this.parent=null,this.href=null,this.children=[]}}class s{constructor(t){this.root=t,this.active=t}up(){this.active.parent&&(this.active=this.active.parent)}down(){this.active.children.length&&(this.active=this.active.children[0])}left(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t>0&&(this.active=this.active.parent.children[t-1])}}right(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t<this.active.parent.children.length-1&&(this.active=this.active.parent.children[t+1])}}activateLink(){this.active.href&&(window.location.href=this.active.href)}}class a{constructor(t){this.node=t,this.tree=(t=>{const i=t.hasAttribute("data-owns")?t:t.querySelector("[data-owns]");return i?new s(e(i)):console.warn("no data-owns attribute in tree:",t)})(t),this.node.addEventListener("keydown",this.move.bind(this)),this.node.addEventListener("focusin",(()=>{this.node.setAttribute("tabindex","-1"),this.highlight(!0)})),this.node.addEventListener("focusout",(()=>{this.highlight(!1),this.node.setAttribute("tabindex","0")}))}active(){return this.tree.active}move(t){switch(this.highlight(!1),[32,37,38,39,40,13,32].indexOf(t.keyCode)>-1&&t.preventDefault(),t.keyCode){case 37:this.tree.left();break;case 38:this.tree.up();break;case 39:this.tree.right();break;case 40:this.tree.down();break;case 32:case 13:this.tree.activateLink()}this.highlight(!0)}highlight(t){const e=this.active().name===this.node.getAttribute("data-owns-id")?this.node:this.node.querySelector(`[data-owns-id="${this.active().name}"]`);!0===t&&(e.setAttribute("tabindex","0"),e.classList.add("is-activedescendant"),e.focus()),!1===t&&(e.setAttribute("tabindex","-1"),e.classList.remove("is-activedescendant"))}}return t.attachNavigator=function(t){new a(t),t.setAttribute("tabindex","0")},Object.defineProperty(t,"__esModule",{value:!0}),t}({});

@@ -38,5 +38,7 @@ const fs = require('fs');

<p>Another typical use case are diagrams created with D3 such as the simple (pre-rendered) tree diagram below. Of course it is also possible to <a href="./d3/">do this client-side</a>.</p>
<svg data-treewalker="" id="d3" tabindex="0" role="tree" aria-label="A simple tree diagram about encyclopedia structure" aria-description="Focus to navigate with arrow keys." width="300" height="300"><g transform="translate(-40,40)"><path class="link" d="M128.57142857142856,100C128.57142857142856,0 171.42857142857142,100 171.42857142857142,0"></path><path class="link" d="M214.28571428571428,100C214.28571428571428,0 171.42857142857142,100 171.42857142857142,0"></path><path class="link" d="M85.71428571428571,200C85.71428571428571,100 128.57142857142856,200 128.57142857142856,100"></path><path class="link" d="M171.42857142857142,200C171.42857142857142,100 128.57142857142856,200 128.57142857142856,100"></path><g class="node node--internal" id="encyclopedia" role="treeitem" aria-label="encyclopedia" aria-owns="culture science" transform="translate(171.42857142857142,0)"><circle r="2.5"></circle><text dy="-1em" x="36" style="text-anchor: end;">encyclopedia</text></g><g class="node node--internal" id="culture" role="treeitem" aria-label="culture" aria-owns="art craft" transform="translate(128.57142857142856,100)"><circle r="2.5"></circle><text dy="-1em" x="21" style="text-anchor: end;">culture</text></g><g class="node node--leaf" id="science" role="treeitem" aria-label="science" aria-owns="" transform="translate(214.28571428571428,100)"><circle r="2.5"></circle><text dy="0.3em" x="8" style="text-anchor: start;">science</text></g><g class="node node--leaf" id="art" role="treeitem" aria-label="art" aria-owns="" transform="translate(85.71428571428571,200)"><circle r="2.5"></circle><text dy="0.3em" x="8" style="text-anchor: start;">art</text></g><g class="node node--leaf" id="craft" role="treeitem" aria-label="craft" aria-owns="" transform="translate(171.42857142857142,200)"><circle r="2.5"></circle><text dy="0.3em" x="8" style="text-anchor: start;">craft</text></g></g></svg> <h2>An equation in SVG</h2>
<svg data-treewalker="" id="d3" role="tree" aria-label="A simple tree diagram about encyclopedia structure" aria-description="Focus to navigate with arrow keys." data-owns="encyclopedia" width="300" height="300" class=""><g transform="translate(-40,40)"><path class="link" d="M128.57142857142856,100C128.57142857142856,0 171.42857142857142,100 171.42857142857142,0"></path><path class="link" d="M214.28571428571428,100C214.28571428571428,0 171.42857142857142,100 171.42857142857142,0"></path><path class="link" d="M85.71428571428571,200C85.71428571428571,100 128.57142857142856,200 128.57142857142856,100"></path><path class="link" d="M171.42857142857142,200C171.42857142857142,100 128.57142857142856,200 128.57142857142856,100"></path><g class="node node--internal" data-owns-id="encyclopedia" role="treeitem" aria-label="encyclopedia" data-owns="culture science" transform="translate(171.42857142857142,0)"><circle r="2.5"></circle><text dy="-1em" x="36" style="text-anchor: end;">encyclopedia</text></g><g class="node node--internal" data-owns-id="culture" role="treeitem" aria-label="culture" data-owns="art craft" transform="translate(128.57142857142856,100)"><circle r="2.5"></circle><text dy="-1em" x="21" style="text-anchor: end;">culture</text></g><g class="node node--leaf" data-owns-id="science" role="treeitem" aria-label="science" data-owns="" transform="translate(214.28571428571428,100)"><circle r="2.5"></circle><text dy="0.3em" x="8" style="text-anchor: start;">science</text></g><g class="node node--leaf" data-owns-id="art" role="treeitem" aria-label="art" data-owns="" transform="translate(85.71428571428571,200)"><circle r="2.5"></circle><text dy="0.3em" x="8" style="text-anchor: start;">art</text></g><g class="node node--leaf" data-owns-id="craft" role="treeitem" aria-label="craft" data-owns="" transform="translate(171.42857142857142,200)"><circle r="2.5"></circle><text dy="0.3em" x="8" style="text-anchor: start;">craft</text></g></g></svg>
<h2>An equation in SVG</h2>
<p>Another typical use case is equational content such as the following SVG describing the solution to the quadratic equation, created with <a href="https://www.mathjax.org">MathJax</a> and <a href="https://github.com/zorkow/speech-rule-engine/">Speech Rule Engine</a> (with additional post-processing, see the doc sources here).</p>
${out.svg
.replace('role="tree"', 'role="tree" data-treewalker')
.replace('focusable="false"', '')

@@ -48,2 +50,3 @@ .replace(/data-semantic-(.*?)="(.*?)" /g, '')}

${out.chtml
.replace('role="tree"', 'role="tree" data-treewalker')
.replace(/ aria-hidden="true"/g, '')

@@ -54,3 +57,3 @@ .replace(/data-semantic-(.*?)="(.*?)" /g, '')}

<p>It is also possible to leverage tikz for this purpose, combining special macros with dvisvgm. The following is a simple tree diagram; you can find the TeX source in the repository's docs folder.</p>
${fs.readFileSync(__dirname + '/../tikz/tree.svg').toString().replace('<![CDATA[','').replace(']]>','').replace('<?xml version=\'1.0\' encoding=\'UTF-8\'?>','').replace('<svg', '<svg tabindex="0" data-treewalker="" id="tikz"' )}
${fs.readFileSync(__dirname + '/../tikz/tree.svg').toString().replace('<![CDATA[','').replace(']]>','').replace('<?xml version=\'1.0\' encoding=\'UTF-8\'?>','').replace('<svg', '<svg data-treewalker="" id="tikz"' )}
<script type="module" src="example.js"></script>

@@ -57,0 +60,0 @@ </body>

@@ -12,3 +12,3 @@ const sre = require('speech-rule-engine');

const rewrite = require('sre-to-tree');
const rewrite = require('@krautzource/sre-to-tree');

@@ -15,0 +15,0 @@ // TeX to MathML

@@ -1,157 +0,1 @@

/**
* Extracts the abstract tree from the DOM node (or a descendant) with aria-owns.
* @param {Node} node The target node.
* @param {Tree} abstractTree The abstract tree that stores the relevant (subtree) structure.
* node ids.
*/
const extractAbstractTree = (node) => {
const treebase = node.hasAttribute('aria-owns')
? node
: node.querySelector('[aria-owns]');
if (!treebase) return console.warn('no aria-owns attribut in tree:', node);
return new abstractTree(recurseNodeToExtractTree(treebase));
};
/**
* Recurses through DOM node to create abstract tree.
* @param {Node} node The target node (with aria-owns attribute).
*/
const recurseNodeToExtractTree = (node) => {
const parent = new abstractNode(node.id);
if (node.tagName.toUpperCase() === 'A' && node.getAttribute('href') !== '') {
parent.href = node.getAttribute('href');
node.setAttribute('tabindex', '-1');
}
if (!node.getAttribute('aria-owns')) {
return parent;
}
node
.getAttribute('aria-owns')
.split(' ')
.forEach((id) => {
const child = document.getElementById(id);
const newnode = recurseNodeToExtractTree(child);
newnode.parent = parent;
parent.children.push(newnode);
});
return parent;
};
/**
* The basic tree for the light walker.
*/
class abstractNode {
constructor(name) {
this.name = name;
this.parent = null;
this.href = null;
this.children = [];
}
}
class abstractTree {
constructor(root) {
this.root = root;
this.active = root;
}
up() {
if (this.active.parent) {
this.active = this.active.parent;
}
}
down() {
if (this.active.children.length) {
this.active = this.active.children[0];
}
}
left() {
if (this.active.parent) {
let index = this.active.parent.children.indexOf(this.active);
if (index > 0) {
this.active = this.active.parent.children[index - 1];
}
}
}
right() {
if (this.active.parent) {
let index = this.active.parent.children.indexOf(this.active);
if (index < this.active.parent.children.length - 1) {
this.active = this.active.parent.children[index + 1];
}
}
}
activateLink() {
if (this.active.href) {
window.location.href = this.active.href;
}
}
}
/**
* Attaches a navigator to the DOM node.
* @param {Node} node The target node.
*/
function attachNavigator(node) {
new navigator(node);
node.setAttribute('tabindex', '0');
}
class navigator {
constructor(node) {
this.node = node;
this.tree = extractAbstractTree(node);
this.node.addEventListener('keydown', this.move.bind(this));
this.node.addEventListener('focusin', this.highlight.bind(this, true));
this.node.addEventListener('focusout', this.highlight.bind(this, false));
}
active() {
return this.tree.active;
}
move(event) {
this.highlight(false);
if ([32, 37, 38, 39, 40, 13, 32].indexOf(event.keyCode) > -1) {
event.preventDefault();
}
switch (event.keyCode) {
case 37: //left
this.tree.left();
break;
case 38: //up
this.tree.up();
break;
case 39: //right
this.tree.right();
break;
case 40: //down
this.tree.down();
break;
case 32: // space
case 13: //enter
this.tree.activateLink();
break;
}
this.highlight(true);
this.node.setAttribute('aria-activedescendant', this.active().name);
}
highlight(boolean) {
const activedescendant =
this.active().name === this.node.id
? this.node
: this.node.querySelector('#' + this.active().name);
if (boolean === true) activedescendant.classList.add('is-activedescendant');
if (boolean === false)
activedescendant.classList.remove('is-activedescendant');
}
}
export { attachNavigator };
const t=i=>{const s=new e(i.getAttribute("data-owns-id"));"A"===i.tagName.toUpperCase()&&""!==i.getAttribute("href")&&(s.href=i.getAttribute("href"),i.setAttribute("tabindex","-1"));const a=i.getAttribute("data-owns");return a?(a.split(" ").forEach((e=>{const i=document.querySelector(`[data-owns-id="${e}"]`),a=t(i);a.parent=s,s.children.push(a)})),s):s};class e{constructor(t){this.name=t,this.parent=null,this.href=null,this.children=[]}}class i{constructor(t){this.root=t,this.active=t}up(){this.active.parent&&(this.active=this.active.parent)}down(){this.active.children.length&&(this.active=this.active.children[0])}left(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t>0&&(this.active=this.active.parent.children[t-1])}}right(){if(this.active.parent){let t=this.active.parent.children.indexOf(this.active);t<this.active.parent.children.length-1&&(this.active=this.active.parent.children[t+1])}}activateLink(){this.active.href&&(window.location.href=this.active.href)}}function s(t){new a(t),t.setAttribute("tabindex","0")}class a{constructor(e){this.node=e,this.tree=(e=>{const s=e.hasAttribute("data-owns")?e:e.querySelector("[data-owns]");return s?new i(t(s)):console.warn("no data-owns attribute in tree:",e)})(e),this.node.addEventListener("keydown",this.move.bind(this)),this.node.addEventListener("focusin",(()=>{this.node.setAttribute("tabindex","-1"),this.highlight(!0)})),this.node.addEventListener("focusout",(()=>{this.highlight(!1),this.node.setAttribute("tabindex","0")}))}active(){return this.tree.active}move(t){switch(this.highlight(!1),[32,37,38,39,40,13,32].indexOf(t.keyCode)>-1&&t.preventDefault(),t.keyCode){case 37:this.tree.left();break;case 38:this.tree.up();break;case 39:this.tree.right();break;case 40:this.tree.down();break;case 32:case 13:this.tree.activateLink()}this.highlight(!0)}highlight(t){const e=this.active().name===this.node.getAttribute("data-owns-id")?this.node:this.node.querySelector(`[data-owns-id="${this.active().name}"]`);!0===t&&(e.setAttribute("tabindex","0"),e.classList.add("is-activedescendant"),e.focus()),!1===t&&(e.setAttribute("tabindex","-1"),e.classList.remove("is-activedescendant"))}}export{s as attachNavigator};
/**
* Extracts the abstract tree from the DOM node (or a descendant) with aria-owns.
* Extracts the abstract tree from the DOM node (or a descendant) with data-owns.
* @param {Node} node The target node.
* @param {Tree} abstractTree The abstract tree that stores the relevant (subtree) structure.
* @returns {Tree} abstractTree The abstract tree that stores the relevant (subtree) structure.
* node ids.
*/
export const extractAbstractTree = (node) => {
const treebase = node.hasAttribute('aria-owns')
const treebase = node.hasAttribute('data-owns')
? node
: node.querySelector('[aria-owns]');
if (!treebase) return console.warn('no aria-owns attribut in tree:', node);
: node.querySelector('[data-owns]');
if (!treebase) return console.warn('no data-owns attribute in tree:', node);
return new abstractTree(recurseNodeToExtractTree(treebase));

@@ -17,7 +17,8 @@ };

* Recurses through DOM node to create abstract tree.
* @param {Node} node The target node (with aria-owns attribute).
* @param {Node} node A node (with data-owns attribute).
* @returns {abstractNode} An abstract tree node.
*/
const recurseNodeToExtractTree = (node) => {
const parent = new abstractNode(node.id);
const parent = new abstractNode(node.getAttribute('data-owns-id'));
if (node.tagName.toUpperCase() === 'A' && node.getAttribute('href') !== '') {

@@ -27,14 +28,12 @@ parent.href = node.getAttribute('href');

}
if (!node.getAttribute('aria-owns')) {
const owns = node.getAttribute('data-owns');
if (!owns) {
return parent;
}
node
.getAttribute('aria-owns')
.split(' ')
.forEach((id) => {
const child = document.getElementById(id);
const newnode = recurseNodeToExtractTree(child);
newnode.parent = parent;
parent.children.push(newnode);
});
owns.split(' ').forEach((id) => {
const child = document.querySelector(`[data-owns-id="${id}"]`);
const newnode = recurseNodeToExtractTree(child);
newnode.parent = parent;
parent.children.push(newnode);
});
return parent;

@@ -41,0 +40,0 @@ };

@@ -17,4 +17,10 @@ import { extractAbstractTree } from './abstractTree.js';

this.node.addEventListener('keydown', this.move.bind(this));
this.node.addEventListener('focusin', this.highlight.bind(this, true));
this.node.addEventListener('focusout', this.highlight.bind(this, false));
this.node.addEventListener('focusin', () => {
this.node.setAttribute('tabindex', '-1');
this.highlight(true);
});
this.node.addEventListener('focusout', () => {
this.highlight(false);
this.node.setAttribute('tabindex', '0');
});
}

@@ -52,3 +58,2 @@

this.highlight(true);
this.node.setAttribute('aria-activedescendant', this.active().name);
}

@@ -58,9 +63,15 @@

const activedescendant =
this.active().name === this.node.id
this.active().name === this.node.getAttribute('data-owns-id')
? this.node
: this.node.querySelector('#' + this.active().name);
if (boolean === true) activedescendant.classList.add('is-activedescendant');
if (boolean === false)
: this.node.querySelector(`[data-owns-id="${this.active().name}"]`);
if (boolean === true) {
activedescendant.setAttribute('tabindex', '0');
activedescendant.classList.add('is-activedescendant');
activedescendant.focus();
}
if (boolean === false) {
activedescendant.setAttribute('tabindex', '-1');
activedescendant.classList.remove('is-activedescendant');
}
}
}
{
"name": "@krautzource/aria-tree-walker",
"version": "2.0.0",
"version": "3.0.0",
"description": "A lightweight walker for labeled aria(-owns) trees",

@@ -14,3 +14,3 @@ "main": "dist/aria-tree-walker.cjs.js",

"postbuild": "npm run declarations",
"docs": "node docs/source/generateDocs.js && npx rollup -c rollup.config.js",
"docs": "node docs/source/generateDocs.js && npx rollup -c rollup.config.js && cp ./dist/aria-tree-walker.esm.js ./docs/treewalker.js",
"release": "standard-version"

@@ -40,2 +40,3 @@ },

"devDependencies": {
"@krautzource/sre-to-tree": "^2.0.0",
"ava": "^3.15.0",

@@ -50,3 +51,2 @@ "jsdom": "^16.4.0",

"speech-rule-engine": "^3.2.0-beta.2",
"sre-to-tree": "@krautzource/sre-to-tree^1",
"standard-version": "^9.1.1",

@@ -53,0 +53,0 @@ "typescript": "^4.2.2"

# aria-tree-walker
A lightweight walker for labeled aria(-owns) trees.
A lightweight walker for labeled ARIA trees.
Reads the aria-owns structure of an aria tree where every node is labeled. Provides a breadth-first tree walker using arrow keys. AT users will get the full label in browse mode and can switch into focus mode to explore (on almost all major browser+AT combinations).
Reads the structure of an [ARIA tree](https://www.w3.org/TR/wai-aria-1.2/#tree) where every node is labeled. Provides a breadth-first tree walker using arrow keys. AT users will get the full label in browse mode and can switch into focus mode to explore (on almost all major browser+AT combinations).

@@ -18,5 +18,5 @@ ## Usage

The navigator extracts an abtract tree of the aria-owns relationships which is used to provide keyboard navigation and (accessible) focus management, i.e., active-descendant management.
The navigator extracts an abtract tree based on suitably prepared data-owns and data-owns-id attributes. The abstract tree is used to provide keyboard navigation and (accessible) focus management (using the "roving tabindex" technique).
The active descendant will get a class of `is-activedescendant` for (visual) styling purposes.
The active tree node will get a class of `is-activedescendant` for (visual) styling purposes.

@@ -27,7 +27,7 @@ ## Content expectations

- The DOM node **must** either have an aria-owns attribute or have a descendant with an aria-owns attribute (which will serve as de-facto root - having a wrapping node around the "real" aria-owns root can make things easier for authoring/design).
- The DOM node **must** be focusable, e.g., have `tabindex="0"`. (While it won't throw an error, it will not work without focus.)
- The "aria-owns tree" **should** be "full", i.e., navigation stops at owned elements without aria-owns attribute. (Could be refined in the future.)
- Each node with aria-owns attribute **should** have suitable roles and ARIA markup, e.g., `role="tree"` and `role="treeitem"` as well as an `aria-label` with a suitable accessible name (to get a "flat" name).
- Visual highlighting is handled by author CSS using `.is-activedescendant`. (This could be refined in the future.)
- The DOM node **must** either have a data-owns attribute or have a descendant with a data-owns attribute (which will serve as de-facto root - having a wrapping node around the "real" root can make things easier for authoring/design).
- The data-owns attribute contains a space-separated list of identifiers, matching data-owns-id attribute values on descendant nodes.
- The "data-owns tree" **should** be "full" since navigation stops at elements without data-owns attribute.
- Each node with data-owns attribute **should** have suitable roles and ARIA markup, e.g., `role="tree"` and `role="treeitem"` as well as an `aria-label` with a suitable accessible name (to get a "flat" name).
- Visual highlighting is handled by author CSS using `.is-activedescendant`.

@@ -34,0 +34,0 @@ ## User Experience

@@ -50,4 +50,3 @@ const sirv = require('sirv');

const activedescendantId = await page.evaluate(() => {
return document.querySelector('[data-treewalker]')
.ariaActiveDescendantElement.id;
return document.activeElement.getAttribute('data-owns-id');
});

@@ -59,2 +58,7 @@ t.is(activedescendantId, 'treeitem1');

await page.goto('localhost:8080/test/');
let tabindex = await page.evaluate(() => {
return document
.querySelector('a').getAttribute('tabindex');
});
t.is(tabindex, '-1');
await page.keyboard.press('Tab');

@@ -64,9 +68,7 @@ await page.keyboard.press('Tab');

await page.keyboard.press('ArrowRight');
const tabindex = await page.evaluate(() => {
tabindex = await page.evaluate(() => {
return document
.querySelectorAll('[data-treewalker]')[1]
.ariaActiveDescendantElement.getAttribute('tabindex');
.querySelector('a').getAttribute('tabindex');
});
console.log(tabindex);
t.is(tabindex, '-1');
t.is(tabindex, '0');
});

@@ -73,0 +75,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc