Focus Next Element In Tab Index

后端 未结 20 2095
失恋的感觉
失恋的感觉 2020-11-27 03:06

I am trying to move the focus to the next element in the tab sequence based upon the current element which has focus. Thus far I have not turned up anything in my searches.<

20条回答
  •  遥遥无期
    2020-11-27 03:23

    Necromancing.
    I have a buch of 0-tabIndexes, which I wanted to navigate by keyboard.
    Since in that case, only the ORDER of the elements mattered, I did it using document.createTreeWalker

    So first you create the filter (you only want [visible] elements, which have an attribute "tabIndex" with a NUMERICAL value.

    Then you set the root node, beyond which you don't want to search. In my case, this.m_tree is a ul-element containing a toggable tree. If you want the entire document instead, just replace this.m_tree with document.documentElement.

    Then you set the current node to the current active element:

    ni.currentNode = el; // el = document.activeElement
    

    Then you return ni.nextNode() or ni.previousNode().

    Note:
    this will NOT return the tabs in the correct order if you have tabIndices != 0 and the element order is NOT the tabIndex order. In case of tabIndex = 0, the tabOrder is always the element order, which is why this works (in that case).

    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 = fn;
        (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(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 ((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 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 currentNode;
        }
    
        return el;
    }
    

    Note that the while-loop

    while (currentNode = ni.nextNode())
    {
        // Additional checks here
        // if(condition) return currentNode;
        // else the loop continues;
        return currentNode; // everything is already filtered down to what we need here
    }
    

    is only there if you want if you have additional criterias which you cannot filter in the filter passed to createTreeWalker.

    Note this is TypeScript, you need to remove all tokens behind colons (:), and between angle-brackets (<>), e.g. or :(node: Node) => number to get valid JavaScript.

    Here as a service, the transpiled JS:

    "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 ((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;
    }
    

提交回复
热议问题