Necromante.
Eu tenho um buch de 0-tabIndexes, que eu queria navegar pelo teclado.
Como, nesse caso, apenas a ORDEM dos elementos importava, fiz isso usandodocument.createTreeWalker
Portanto, primeiro você cria o filtro (você deseja apenas elementos [visíveis], que têm um atributo "tabIndex" com um valor NUMERICAL.
Em seguida, você define o nó raiz, além do qual não deseja pesquisar. No meu caso, this.m_tree
é um elemento ul contendo uma árvore toggable. Se você quiser o documento inteiro, basta substituir this.m_tree
pordocument.documentElement
.
Em seguida, você define o nó atual para o elemento ativo atual:
ni.currentNode = el; // el = document.activeElement
Então você retorna ni.nextNode()
ou ni.previousNode()
.
Observação:
isso NÃO retornará as guias na ordem correta se você tiver tabIndices! = 0 e a ordem do elemento NÃO for a ordem tabIndex. No caso de tabIndex = 0, o tabOrder é sempre a ordem dos elementos, por isso funciona (nesse caso).
protected createFilter(fn?: (node: Node) => number): NodeFilter
{
// Accept all currently filtered elements.
function acceptNode(node: Node): number
{
return NodeFilter.FILTER_ACCEPT;
}
if (fn == null)
fn = acceptNode;
// Work around Internet Explorer wanting a function instead of an object.
// IE also *requires* this argument where other browsers don't.
const safeFilter: NodeFilter = <NodeFilter><any>fn;
(<any>safeFilter).acceptNode = fn;
return safeFilter;
}
protected createTabbingFilter(): NodeFilter
{
// Accept all currently filtered elements.
function acceptNode(node: Node): number
{
if (!node)
return NodeFilter.FILTER_REJECT;
if (node.nodeType !== Node.ELEMENT_NODE)
return NodeFilter.FILTER_REJECT;
if (window.getComputedStyle(<Element>node).display === "none")
return NodeFilter.FILTER_REJECT;
// "tabIndex": "0"
if (!(<Element>node).hasAttribute("tabIndex"))
return NodeFilter.FILTER_SKIP;
let tabIndex = parseInt((<Element>node).getAttribute("tabIndex"), 10);
if (!tabIndex || isNaN(tabIndex) || !isFinite(tabIndex))
return NodeFilter.FILTER_SKIP;
// if ((<Element>node).tagName !== "LI") return NodeFilter.FILTER_SKIP;
return NodeFilter.FILTER_ACCEPT;
}
return this.createFilter(acceptNode);
}
protected getNextTab(el: HTMLElement): HTMLElement
{
let currentNode: Node;
// https://developer.mozilla.org/en-US/docs/Web/API/Document/createNodeIterator
// https://developer.mozilla.org/en-US/docs/Web/API/Document/createTreeWalker
// let ni = document.createNodeIterator(el, NodeFilter.SHOW_ELEMENT);
// let ni = document.createTreeWalker(this.m_tree, NodeFilter.SHOW_ELEMENT);
let ni = document.createTreeWalker(this.m_tree, NodeFilter.SHOW_ELEMENT, this.createTabbingFilter(), false);
ni.currentNode = el;
while (currentNode = ni.nextNode())
{
return <HTMLElement>currentNode;
}
return el;
}
protected getPreviousTab(el: HTMLElement): HTMLElement
{
let currentNode: Node;
let ni = document.createTreeWalker(this.m_tree, NodeFilter.SHOW_ELEMENT, this.createTabbingFilter(), false);
ni.currentNode = el;
while (currentNode = ni.previousNode())
{
return <HTMLElement>currentNode;
}
return el;
}
Observe que o loop while
while (currentNode = ni.nextNode())
{
// Additional checks here
// if(condition) return currentNode;
// else the loop continues;
return <HTMLElement>currentNode; // everything is already filtered down to what we need here
}
está lá apenas se você desejar, se tiver critérios adicionais que não podem ser filtrados no filtro passado para createTreeWalker.
Observe que este é o TypeScript, você precisa remover todos os tokens atrás dos dois pontos (:) e entre colchetes angulares (<>), por exemplo, <Element>
ou:(node: Node) => number
para obter um JavaScript válido.
Aqui como um serviço, o JS transpilado:
"use strict";
function createFilter(fn) {
// Accept all currently filtered elements.
function acceptNode(node) {
return NodeFilter.FILTER_ACCEPT;
}
if (fn == null)
fn = acceptNode;
// Work around Internet Explorer wanting a function instead of an object.
// IE also *requires* this argument where other browsers don't.
const safeFilter = fn;
safeFilter.acceptNode = fn;
return safeFilter;
}
function createTabbingFilter() {
// Accept all currently filtered elements.
function acceptNode(node) {
if (!node)
return NodeFilter.FILTER_REJECT;
if (node.nodeType !== Node.ELEMENT_NODE)
return NodeFilter.FILTER_REJECT;
if (window.getComputedStyle(node).display === "none")
return NodeFilter.FILTER_REJECT;
// "tabIndex": "0"
if (!node.hasAttribute("tabIndex"))
return NodeFilter.FILTER_SKIP;
let tabIndex = parseInt(node.getAttribute("tabIndex"), 10);
if (!tabIndex || isNaN(tabIndex) || !isFinite(tabIndex))
return NodeFilter.FILTER_SKIP;
// if ((<Element>node).tagName !== "LI") return NodeFilter.FILTER_SKIP;
return NodeFilter.FILTER_ACCEPT;
}
return createFilter(acceptNode);
}
function getNextTab(el) {
let currentNode;
// https://developer.mozilla.org/en-US/docs/Web/API/Document/createNodeIterator
// https://developer.mozilla.org/en-US/docs/Web/API/Document/createTreeWalker
// let ni = document.createNodeIterator(el, NodeFilter.SHOW_ELEMENT);
// let ni = document.createTreeWalker(this.m_tree, NodeFilter.SHOW_ELEMENT);
let ni = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_ELEMENT, createTabbingFilter(), false);
ni.currentNode = el;
while (currentNode = ni.nextNode()) {
return currentNode;
}
return el;
}
function getPreviousTab(el) {
let currentNode;
let ni = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_ELEMENT, createTabbingFilter(), false);
ni.currentNode = el;
while (currentNode = ni.previousNode()) {
return currentNode;
}
return el;
}
currentElementId = "";
?