【问题标题】:Focus Next Element In Tab Index聚焦标签索引中的下一个元素
【发布时间】:2011-11-04 17:00:29
【问题描述】:

我正在尝试根据当前具有焦点的元素将焦点移动到选项卡序列中的下一个元素。到目前为止,我还没有在我的搜索中找到任何东西。

function OnFocusOut()
{
    var currentElement = $get(currentElementId); // ID set by OnFocusIn 

    currentElementId = "";
    currentElement.nextElementByTabIndex.focus();
}

当然 nextElementByTabIndex 是这个工作的关键部分。如何找到制表符序列中的下一个元素?该解决方案需要基于使用 JScript 而不是 JQuery。

【问题讨论】:

  • 为什么会有currentElementId = "";这一行?
  • 我不认为任何浏览器都会暴露标签顺序信息——而且浏览器本身使用的算法太复杂而无法复制。也许您可以限制您的要求,例如“只考虑inputbuttontextarea 标签并忽略tabindex 属性”。
  • 我们需要查看您的.newElementByTabIndex 代码,因为这不起作用。
  • 再一次,也许对特定标签的限制是不必要的——可以检查focus()方法是否存在。
  • @David 这是不存在的功能,因此是我的问题。 :D

标签: javascript jscript.net


【解决方案1】:

我从来没有实现过这个,但我研究了一个类似的问题,这就是我会尝试的。

先试试这个

首先,我想看看您是否可以简单地 fire a keypress event 为当前具有焦点的元素上的 Tab 键。对于不同的浏览器,可能会有不同的方法。

如果这不起作用,你将不得不更加努力......

引用jQuery实现,你必须:

  1. 监听 Tab 和 Shift+Tab
  2. 了解哪些元素是可选项卡的
  3. 了解跳位顺序的工作原理

1。听 Tab 和 Shift+Tab

Tab 和 Shift+Tab 的侦听可能在网络上的其他地方得到了很好的介绍,所以我将跳过这部分。

2。知道哪些元素是可选项卡

知道哪些元素是可选项卡的比较棘手。基本上,如果一个元素是可聚焦的并且没有设置tabindex="-1" 属性,则它是可选项卡的。那么我们必须问哪些元素是可聚焦的。以下元素是可聚焦的:

  • inputselecttextareabuttonobject 未禁用的元素。
  • 具有href 或具有tabindex 集合的数值的aarea 元素。
  • 任何具有tabindex 数值集的元素。

此外,元素只有在以下情况下才可聚焦:

  • 它的祖先都不是display: none
  • visibility 的计算值为visible。这意味着要设置visibility 的最近祖先必须具有visible 的值。如果没有祖先设置visibility,则计算值为visible

更多细节在另一个Stack Overflow answer

3。了解跳位顺序的工作原理

文档中元素的 Tab 键顺序由 tabindex 属性控制。如果没有设置值,tabindex 实际上是0

文档的tabindex 顺序为:1、2、3、...、0。

最初,当body 元素(或无元素)具有焦点时,Tab 顺序中的第一个元素是最低的非零tabindex。如果多个元素具有相同的tabindex,那么您将按文档顺序进行,直到到达具有该tabindex 的最后一个元素。然后您移动到下一个最低的tabindex 并继续该过程。最后,以零(或空)tabindex 的元素结束。

【讨论】:

  • 82 次投票,但没有一条评论表明 哪些 建议的解决方案确实有效?
  • 我也在想同样的事情?不知道哪个修复是正确的
【解决方案2】:

这是我为此目的而构建的:

function focusNextElement () {
    //add all elements we want to include in our selection
    var focussableElements = 'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])';
    if (document.activeElement && document.activeElement.form) {
        var focussable = Array.prototype.filter.call(document.activeElement.form.querySelectorAll(focussableElements),
        function (element) {
            //check for visibility while always include the current activeElement 
            return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement
        });
        var index = focussable.indexOf(document.activeElement);
        if(index > -1) {
           var nextElement = focussable[index + 1] || focussable[0];
           nextElement.focus();
        }                    
    }
}

特点:

  • 可配置的可聚焦元素集
  • 不需要 jQuery
  • 适用于所有现代浏览器
  • 快速轻量

【讨论】:

  • 这是最有效且资源友好的解决方案。谢谢!这是我的完整工作脚本:stackoverflow.com/a/40686327/1589669
  • 我在下面添加了一个 sn-p 以包括按显式 TabIndex focussable.sort(sort_by_TabIndex) 进行排序
  • 最好的! 必须那么复杂:nextElementSibling 可能不可聚焦,下一个可聚焦的可能不是兄弟。
  • 好方法,但它应该允许任何不是hidden 类型的输入,并且还包括textareaselect
【解决方案3】:

没有 jquery: 首先,在您的可选项卡元素上,添加class="tabable" 这将让我们稍后选择它们。 (不要忘记下面代码中的“.”类选择器前缀)

var lastTabIndex = 10;
function OnFocusOut()
{
    var currentElement = $get(currentElementId); // ID set by OnFOcusIn
    var curIndex = currentElement.tabIndex; //get current elements tab index
    if(curIndex == lastTabIndex) { //if we are on the last tabindex, go back to the beginning
        curIndex = 0;
    }
    var tabbables = document.querySelectorAll(".tabable"); //get all tabable elements
    for(var i=0; i<tabbables.length; i++) { //loop through each element
        if(tabbables[i].tabIndex == (curIndex+1)) { //check the tabindex to see if it's the element we want
            tabbables[i].focus(); //if it's the one we want, focus it and exit the loop
            break;
        }
    }
}

【讨论】:

  • 一种不需要为每个元素添加名称的解决方案(因为如果可行的话,有很多方法可以实现)将是理想的。
  • 好的,这是表格吗?如果您想要的所有元素都是输入元素,则可以将行 var tabbables = document.getElementsByName("tabable"); 替换为 var tabbables = document.getElementsByTagName("input");
  • var tabbables = document.querySelectorAll("input, textarea, button") // IE8+ ,在不修改 HTML 的情况下获取所有标签的引用。
  • class="tabbable" 而不是使用名称属性
  • 请注意,使用 flexbox 元素的顺序在 DOM 中的顺序与在浏览器中的视觉顺序不同。当您使用 flexbox 更改元素的顺序时,仅选择下一个可选项卡元素不起作用。
【解决方案4】:

我创建了一个simple jQuery plugin,它就是这样做的。它使用 jQuery UI 的 ':tabbable' 选择器来查找下一个 'tabbable' 元素并选择它。

示例用法:

// Simulate tab key when element is clicked 
$('.myElement').bind('click', function(event){
    $.tabNext();
    return false;
});

【讨论】:

  • 这是迄今为止最好的解决方案。
  • 对于那些不想安装插件的人的简短预览:var selectables = $(':focusable');var currentIndex = selectables.index($(':focus'));selectables.eq(currentIndex + 1).focus(); 该插件具有错误检查和更多选项。
【解决方案5】:

答案的核心在于寻找下一个元素:

  function findNextTabStop(el) {
    var universe = document.querySelectorAll('input, button, select, textarea, a[href]');
    var list = Array.prototype.filter.call(universe, function(item) {return item.tabIndex >= "0"});
    var index = list.indexOf(el);
    return list[index + 1] || list[0];
  }

用法:

var nextEl = findNextTabStop(element);
nextEl.focus();

请注意,我不在乎优先考虑 tabIndex

【讨论】:

  • 如果tabindex顺序与文档顺序相反怎么办?我认为数组必须按 tabindex 编号然后按文档顺序排序
  • 是的,那会更“符合规范”。我不确定边缘情况、父元素等
  • 如果不是这些标签之一的项目具有 tabindex 属性怎么办?
  • @MattPennington 它会被忽略。过滤器是(一种尝试)加快搜索速度,请随意调整。
【解决方案6】:

Tabbable 是一个小型 JS 包,它为您提供所有可标签元素的列表按标签顺序。因此,您可以在该列表中找到您的元素,然后专注于下一个列表条目。

该包正确处理其他答案中提到的复杂边缘情况(例如,没有祖先可以是display: none)。而且它不依赖于jQuery!

在撰写本文时(版本 1.1.1),它有一个警告,它不支持 IE8,并且浏览器错误阻止它正确处理 contenteditable

【讨论】:

    【解决方案7】:

    您似乎可以检查元素的tabIndex 属性以确定它是否可聚焦。不可聚焦的元素的 tabindex 为“-1”。

    那么你只需要知道制表位的规则:

    • tabIndex="1" 具有最高优先级。
    • tabIndex="2" 的优先级次之。
    • tabIndex="3" 是下一个,依此类推。
    • tabIndex="0"(或默认选项卡)具有最低优先级。
    • tabIndex="-1"(或默认不可制表符)不充当制表位。
    • 对于具有相同 tabIndex 的两个元素,在 DOM 中首先出现的元素具有更高的优先级。

    这是一个如何使用纯 Javascript 按顺序构建制表位列表的示例:

    function getTabStops(o, a, el) {
        // Check if this element is a tab stop
        if (el.tabIndex > 0) {
            if (o[el.tabIndex]) {
                o[el.tabIndex].push(el);
            } else {
                o[el.tabIndex] = [el];
            }
        } else if (el.tabIndex === 0) {
            // Tab index "0" comes last so we accumulate it seperately
            a.push(el);
        }
        // Check if children are tab stops
        for (var i = 0, l = el.children.length; i < l; i++) {
            getTabStops(o, a, el.children[i]);
        }
    }
    
    var o = [],
        a = [],
        stops = [],
        active = document.activeElement;
    
    getTabStops(o, a, document.body);
    
    // Use simple loops for maximum browser support
    for (var i = 0, l = o.length; i < l; i++) {
        if (o[i]) {
            for (var j = 0, m = o[i].length; j < m; j++) {
                stops.push(o[i][j]);
            }
        }
    }
    for (var i = 0, l = a.length; i < l; i++) {
        stops.push(a[i]);
    }
    

    我们首先遍历 DOM,按顺序收集所有制表位及其索引。然后我们组装最终列表。请注意,我们将带有tabIndex="0" 的项目添加到列表的最后,在带有tabIndex 1、2、3 等的项目之后。

    对于一个完整的示例,您可以使用“输入”键在其中进行切换,请查看此fiddle

    【讨论】:

      【解决方案8】:

      正如上面评论中提到的,我认为任何浏览器都不会公开标签顺序信息。下面是浏览器为按 Tab 键顺序获取下一个元素所做的简化近似:

      var allowedTags = {input: true, textarea: true, button: true};
      
      var walker = document.createTreeWalker(
        document.body,
        NodeFilter.SHOW_ELEMENT,
        {
          acceptNode: function(node)
          {
            if (node.localName in allowedTags)
              return NodeFilter.FILTER_ACCEPT;
            else
              NodeFilter.FILTER_SKIP;
          }
        },
        false
      );
      walker.currentNode = currentElement;
      if (!walker.nextNode())
      {
        // Restart search from the start of the document
        walker.currentNode = walker.root;
        walker.nextNode();
      }
      if (walker.currentNode && walker.currentNode != walker.root)
        walker.currentNode.focus();
      

      这仅考虑一些标签并忽略tabindex 属性,但根据您要实现的目标可能就足够了。

      【讨论】:

        【解决方案9】:
        function focusNextElement(){
          var focusable = [].slice.call(document.querySelectorAll("a, button, input, select, textarea, [tabindex], [contenteditable]")).filter(function($e){
            if($e.disabled || ($e.getAttribute("tabindex") && parseInt($e.getAttribute("tabindex"))<0)) return false;
            return true;
          }).sort(function($a, $b){
            return (parseFloat($a.getAttribute("tabindex") || 99999) || 99999) - (parseFloat($b.getAttribute("tabindex") || 99999) || 99999);
          });
          var focusIndex = focusable.indexOf(document.activeElement);
          if(focusable[focusIndex+1]) focusable[focusIndex+1].focus();
        };
        

        【讨论】:

          【解决方案10】:

          可以在组件上设置tabindex 属性。它指定在选择一个并按 Tab 时应以何种顺序迭代输入组件。大于 0 的值保留用于自定义导航,0 是“按自然顺序”(如果为第一个元素设置,则行为会有所不同),-1 表示键盘不可聚焦:

          <!-- navigate with tab key: -->
          <input tabindex="1" type="text"/>
          <input tabindex="2" type="text"/>
          

          它也可以设置为文本输入字段以外的其他内容,但它会在那里做什么并不是很明显,如果有的话。即使导航可以正常工作,对于非常明显的用户输入元素之外的任何其他内容,使用“自然顺序”可能会更好。

          不,您根本不需要 JQuery 或任何脚本来支持这种自定义导航路径。您可以在没有任何 JavaScript 支持的情况下在服务器端实现它。另一方面,该属性在 React 框架中也可以正常工作,但不需要它。

          【讨论】:

            【解决方案11】:

            这是我在 SO 上的第一篇文章,因此我没有足够的声誉来评论已接受的答案,但我不得不将代码修改为以下内容:

            export function focusNextElement () {
              //add all elements we want to include in our selection
              const focussableElements = 
                'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled])'
              if (document.activeElement && document.activeElement.form) {
                  var focussable = Array.prototype.filter.call(
                    document.activeElement.form.querySelectorAll(focussableElements),
                  function (element) {
                      // if element has tabindex = -1, it is not focussable
                      if ( element.hasAttribute('tabindex') && element.tabIndex === -1 ){
                        return false
                      }
                      //check for visibility while always include the current activeElement 
                      return (element.offsetWidth > 0 || element.offsetHeight > 0 || 
                        element === document.activeElement)
                  });
                  console.log(focussable)
                  var index = focussable.indexOf(document.activeElement);
                  if(index > -1) {
                     var nextElement = focussable[index + 1] || focussable[0];
                     console.log(nextElement)
                     nextElement.focus()
                  }                    
              }
            }
            

            将 var 更改为常量并不重要。主要的变化是我们去掉了检查 tabindex != "-1" 的选择器。然后,如果元素具有属性 tabindex 并且它被设置为“-1”,我们不认为它是可聚焦的。

            我需要更改它的原因是,当将 tabindex="-1" 添加到 &lt;input&gt; 时,该元素仍然被认为是可聚焦的,因为它匹配 "input[type=text]:not([disabled]) "选择器。我的更改相当于“如果我们是一个非禁用文本输入,并且我们有一个 tabIndex 属性,并且该属性的值为 -1,那么我们不应该被认为是可聚焦的。

            我相信当接受答案的作者编辑他们的答案以解释 tabIndex 属性时,他们没有正确地这样做。如果不是这种情况,请告诉我

            【讨论】:

              【解决方案12】:

              这里是关注下一个元素的更完整版本。它遵循规范指南并使用 tabindex 正确排序元素列表。如果要获取前一个元素,还定义了一个反向变量。

              function focusNextElement( reverse, activeElem ) {
                /*check if an element is defined or use activeElement*/
                activeElem = activeElem instanceof HTMLElement ? activeElem : document.activeElement;
              
                let queryString = [
                    'a:not([disabled]):not([tabindex="-1"])',
                    'button:not([disabled]):not([tabindex="-1"])',
                    'input:not([disabled]):not([tabindex="-1"])',
                    'select:not([disabled]):not([tabindex="-1"])',
                    '[tabindex]:not([disabled]):not([tabindex="-1"])'
                    /* add custom queries here */
                  ].join(','),
                  queryResult = Array.prototype.filter.call(document.querySelectorAll(queryString), elem => {
                    /*check for visibility while always include the current activeElement*/
                    return elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem === activeElem;
                  }),
                  indexedList = queryResult.slice().filter(elem => {
                    /* filter out all indexes not greater than 0 */
                    return elem.tabIndex == 0 || elem.tabIndex == -1 ? false : true;
                  }).sort((a, b) => {
                    /* sort the array by index from smallest to largest */
                    return a.tabIndex != 0 && b.tabIndex != 0 
                      ? (a.tabIndex < b.tabIndex ? -1 : b.tabIndex < a.tabIndex ? 1 : 0) 
                      : a.tabIndex != 0 ? -1 : b.tabIndex != 0 ? 1 : 0;
                  }),
                  focusable = [].concat(indexedList, queryResult.filter(elem => {
                    /* filter out all indexes above 0 */
                    return elem.tabIndex == 0 || elem.tabIndex == -1 ? true : false;
                  }));
              
                /* if reverse is true return the previous focusable element
                   if reverse is false return the next focusable element */
                return reverse ? (focusable[focusable.indexOf(activeElem) - 1] || focusable[focusable.length - 1]) 
                  : (focusable[focusable.indexOf(activeElem) + 1] || focusable[0]);
              }
              

              【讨论】:

                【解决方案13】:

                这是对 @Kano@Mx 提供的出色解决方案的潜在增强。如果要保留 TabIndex 排序,请在中间添加此排序:

                // Sort by explicit Tab Index, if any
                var sort_by_TabIndex = function (elementA, elementB) {
                    let a = elementA.tabIndex || 1;
                    let b = elementB.tabIndex || 1;
                    if (a < b) { return -1; }
                    if (a > b) { return 1; }
                    return 0;
                }
                focussable.sort(sort_by_TabIndex);
                

                【讨论】:

                  【解决方案14】:

                  我有一堆 0-tabIndex,我想通过键盘导航。
                  因为在这种情况下,只有元素的顺序很重要,所以我使用document.createTreeWalker

                  所以首先你要创建过滤器(你只需要 [visible] 元素,它们有一个属性“tabIndex”和一个 NUMERICAL 值。

                  然后你设置根节点,你不想在它之外搜索。 在我的例子中,this.m_tree 是一个包含可切换树的 ul 元素。 如果您想要整个文档,只需将 this.m_tree 替换为 document.documentElement

                  然后将当前节点设置为当前活动元素:

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

                  然后你返回ni.nextNode()ni.previousNode()

                  注意:
                  如果您有 tabIndices != 0 并且元素顺序不是 tabIndex 顺序,这将不会以正确的顺序返回选项卡。在 tabIndex = 0 的情况下,tabOrder 始终是元素顺序,这就是它起作用的原因(在这种情况下)。

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

                  注意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
                  }
                  

                  仅当您有无法在传递给 createTreeWalker 的过滤器中过滤的其他条件时才存在。

                  注意这是 TypeScript,您需要删除冒号 (:) 后面和尖括号 () 之间的所有标记,例如&lt;Element&gt;:(node: Node) =&gt; number 获取有效的 JavaScript。

                  这里作为服务,转译后的 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 ((<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;
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    您只需要更改标签索引。 tabindex 全局属性表明它的元素可以被聚焦,以及它参与顺序键盘导航的位置(通常使用 Tab 键,因此得名)。

                    <p>Click anywhere in this pane, then try tabbing through the elements.</p>
                    
                    <label>First in tab order:<input type="text"></label>
                    
                    <div tabindex="0">Tabbable due to tabindex.</div>
                    
                    <div>Not tabbable: no tabindex.</div>
                    
                    <label>Third in tab order:<input type="text"></label>

                    know more

                    【讨论】:

                      【解决方案16】:

                      您是否为要循环浏览的每个元素指定了自己的 tabIndex 值? 如果是这样,你可以试试这个:

                      var lasTabIndex = 10; //Set this to the highest tabIndex you have
                      function OnFocusOut()
                      {
                          var currentElement = $get(currentElementId); // ID set by OnFocusIn 
                      
                          var curIndex = $(currentElement).attr('tabindex'); //get the tab index of the current element
                          if(curIndex == lastTabIndex) { //if we are on the last tabindex, go back to the beginning
                              curIndex = 0;
                          }
                          $('[tabindex=' + (curIndex + 1) + ']').focus(); //set focus on the element that has a tab index one greater than the current tab index
                      }
                      

                      你用的是jquery,对吧?

                      【讨论】:

                      • 我们没有使用 JQuery,因为它会破坏应用程序。 ://
                      • 好的,我想我可以重写是不使用jquery,给我一分钟
                      • 我们感兴趣的每个元素都设置了它们的标签索引值。
                      【解决方案17】:

                      忠告:在标签事件期间不要试图控制焦点所在的位置。而是尝试通过将您想要获得焦点的元素的tabIndex 设置为-1 来控制哪些元素是可标签的,哪些元素是不可标签的。例如

                      // `tabContainer` is a container where we want only
                      // element at a time to be tabbable, e.g. a radio menu.
                      
                      tabContainer.addEventListener("focusin", () => {
                        const desired = findDesiredFocusElement();
                      
                        if (!desired) {
                          // Just leave the focus be. We have no preference
                          // at the moment.
                          return;
                        }
                      
                        // Move the focus to the correct element.
                        desired.focus();
                      
                        // Remove all undesired elements from the tab order.
                        for (const undesired of findUndesiredFocusElements()) {
                          // Make it untabbable.
                          undesired.tabIndex = -1;
                        }
                      });
                      
                      tabContainer.addEventListener("focusout", (event) => {
                        for (const element of findRelevantFocusElements()) {
                          // Give each element back their focus capability.
                          element.tabIndex = 0;
                        }
                      });
                      

                      注意:这可能不是最适合您的情况,例如在您的情况下,最好在某些 change 事件中控制选项卡索引,或者 not 以重置 focusout 等上的 tabIndex 状态。

                      更多信息here

                      【讨论】:

                        【解决方案18】:

                        如果你使用库“JQuery”,你可以这样调用:

                        标签:

                        $.tabNext();
                        

                        Shift+Tab:

                        $.tabPrev();
                        

                        <!DOCTYPE html>
                        <html>
                        <body>
                        <script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
                        <script>
                        (function($){
                            'use strict';
                        
                            /**
                             * Focusses the next :focusable element. Elements with tabindex=-1 are focusable, but not tabable.
                             * Does not take into account that the taborder might be different as the :tabbable elements order
                             * (which happens when using tabindexes which are greater than 0).
                             */
                            $.focusNext = function(){
                                selectNextTabbableOrFocusable(':focusable');
                            };
                        
                            /**
                             * Focusses the previous :focusable element. Elements with tabindex=-1 are focusable, but not tabable.
                             * Does not take into account that the taborder might be different as the :tabbable elements order
                             * (which happens when using tabindexes which are greater than 0).
                             */
                            $.focusPrev = function(){
                                selectPrevTabbableOrFocusable(':focusable');
                            };
                        
                            /**
                             * Focusses the next :tabable element.
                             * Does not take into account that the taborder might be different as the :tabbable elements order
                             * (which happens when using tabindexes which are greater than 0).
                             */
                            $.tabNext = function(){
                                selectNextTabbableOrFocusable(':tabbable');
                            };
                        
                            /**
                             * Focusses the previous :tabbable element
                             * Does not take into account that the taborder might be different as the :tabbable elements order
                             * (which happens when using tabindexes which are greater than 0).
                             */
                            $.tabPrev = function(){
                                selectPrevTabbableOrFocusable(':tabbable');
                            };
                        
                            function tabIndexToInt(tabIndex){
                                var tabIndexInded = parseInt(tabIndex);
                                if(isNaN(tabIndexInded)){
                                    return 0;
                                }else{
                                    return tabIndexInded;
                                }
                            }
                        
                            function getTabIndexList(elements){
                                var list = [];
                                for(var i=0; i<elements.length; i++){
                                    list.push(tabIndexToInt(elements.eq(i).attr("tabIndex")));
                                }
                                return list;
                            }
                        
                            function selectNextTabbableOrFocusable(selector){
                                var selectables = $(selector);
                                var current = $(':focus');
                        
                                // Find same TabIndex of remainder element
                                var currentIndex = selectables.index(current);
                                var currentTabIndex = tabIndexToInt(current.attr("tabIndex"));
                                for(var i=currentIndex+1; i<selectables.length; i++){
                                    if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === currentTabIndex){
                                        selectables.eq(i).focus();
                                        return;
                                    }
                                }
                        
                                // Check is last TabIndex
                                var tabIndexList = getTabIndexList(selectables).sort(function(a, b){return a-b});
                                if(currentTabIndex === tabIndexList[tabIndexList.length-1]){
                                    currentTabIndex = -1;// Starting from 0
                                }
                        
                                // Find next TabIndex of all element
                                var nextTabIndex = tabIndexList.find(function(element){return currentTabIndex<element;});
                                for(var i=0; i<selectables.length; i++){
                                    if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === nextTabIndex){
                                        selectables.eq(i).focus();
                                        return;
                                    }
                                }
                            }
                        
                            function selectPrevTabbableOrFocusable(selector){
                                var selectables = $(selector);
                                var current = $(':focus');
                        
                                // Find same TabIndex of remainder element
                                var currentIndex = selectables.index(current);
                                var currentTabIndex = tabIndexToInt(current.attr("tabIndex"));
                                for(var i=currentIndex-1; 0<=i; i--){
                                    if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === currentTabIndex){
                                        selectables.eq(i).focus();
                                        return;
                                    }
                                }
                        
                                // Check is last TabIndex
                                var tabIndexList = getTabIndexList(selectables).sort(function(a, b){return b-a});
                                if(currentTabIndex <= tabIndexList[tabIndexList.length-1]){
                                    currentTabIndex = tabIndexList[0]+1;// Starting from max
                                }
                        
                                // Find prev TabIndex of all element
                                var prevTabIndex = tabIndexList.find(function(element){return element<currentTabIndex;});
                                for(var i=selectables.length-1; 0<=i; i--){
                                    if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === prevTabIndex){
                                        selectables.eq(i).focus();
                                        return;
                                    }
                                }
                            }
                        
                            /**
                             * :focusable and :tabbable, both taken from jQuery UI Core
                             */
                            $.extend($.expr[ ':' ], {
                                data: $.expr.createPseudo ?
                                    $.expr.createPseudo(function(dataName){
                                        return function(elem){
                                            return !!$.data(elem, dataName);
                                        };
                                    }) :
                                    // support: jQuery <1.8
                                    function(elem, i, match){
                                        return !!$.data(elem, match[ 3 ]);
                                    },
                        
                                focusable: function(element){
                                    return focusable(element, !isNaN($.attr(element, 'tabindex')));
                                },
                        
                                tabbable: function(element){
                                    var tabIndex = $.attr(element, 'tabindex'),
                                        isTabIndexNaN = isNaN(tabIndex);
                                    return ( isTabIndexNaN || tabIndex >= 0 ) && focusable(element, !isTabIndexNaN);
                                }
                            });
                        
                            /**
                             * focussable function, taken from jQuery UI Core
                             * @param element
                             * @returns {*}
                             */
                            function focusable(element){
                                var map, mapName, img,
                                    nodeName = element.nodeName.toLowerCase(),
                                    isTabIndexNotNaN = !isNaN($.attr(element, 'tabindex'));
                                if('area' === nodeName){
                                    map = element.parentNode;
                                    mapName = map.name;
                                    if(!element.href || !mapName || map.nodeName.toLowerCase() !== 'map'){
                                        return false;
                                    }
                                    img = $('img[usemap=#' + mapName + ']')[0];
                                    return !!img && visible(img);
                                }
                                return ( /^(input|select|textarea|button|object)$/.test(nodeName) ?
                                    !element.disabled :
                                    'a' === nodeName ?
                                        element.href || isTabIndexNotNaN :
                                        isTabIndexNotNaN) &&
                                    // the element and all of its ancestors must be visible
                                    visible(element);
                        
                                function visible(element){
                                    return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function(){
                                        return $.css(this, 'visibility') === 'hidden';
                                    }).length;
                                }
                            }
                        })(jQuery);
                        </script>
                        
                        <a tabindex="5">5</a><br>
                        <a tabindex="20">20</a><br>
                        <a tabindex="3">3</a><br>
                        <a tabindex="7">7</a><br>
                        <a tabindex="20">20</a><br>
                        <a tabindex="0">0</a><br>
                        
                        <script>
                        var timer;
                        function tab(){
                            window.clearTimeout(timer)
                            timer = window.setInterval(function(){$.tabNext();}, 1000);
                        }
                        function shiftTab(){
                            window.clearTimeout(timer)
                            timer = window.setInterval(function(){$.tabPrev();}, 1000);
                        }
                        </script>
                        <button tabindex="-1" onclick="tab()">Tab</button>
                        <button tabindex="-1" onclick="shiftTab()">Shift+Tab</button>
                        
                        </body>
                        </html>

                        我修改jquery.tabbable插件完成。

                        【讨论】:

                        • this answer 的副本,由该 jQuery 插件的创建者发布。
                        【解决方案19】:

                        我使用这个代码,它依赖于库“JQuery”:

                            $(document).on('change', 'select', function () {
                            let next_select = $(this);
                        // console.log(next_select.toArray())
                            if (!next_select.parent().parent().next().find('select').length) {
                                next_select.parent().parent().parent().next().find('input[type="text"]').click()
                                console.log(next_select.parent().parent().parent().next());
                            } else if (next_select.parent().parent().next().find('select').prop("disabled")) {
                                setTimeout(function () {
                                    next_select.parent().parent().next().find('select').select2('open')
                                }, 1000)
                                console.log('b');
                            } else if (next_select.parent().parent().next().find('select').length) {
                                next_select.parent().parent().next().find('select').select2('open')
                                console.log('c');
                            }
                        });
                        

                        【讨论】:

                        • 您好,如果您能稍微解释一下代码,那就太好了,这将有助于新读者更好地理解。谢谢
                        【解决方案20】:

                        我检查了上述解决方案,发现它们相当冗长。 只需一行代码即可完成:

                        currentElement.nextElementSibling.focus();
                        

                        currentElement.previousElementSibling.focus();
                        

                        这里的 currentElement 可以是任意的,即 document.activeElement 或 this 如果当前元素在函数的上下文中。

                        我使用 keydown 事件跟踪 tab 和 shift-tab 事件 这是一个依赖“JQuery”的sn-p:

                        let cursorDirection = ''
                        $(document).keydown(function (e) {
                            let key = e.which || e.keyCode;
                            if (e.shiftKey) {
                                //does not matter if user has pressed tab key or not.
                                //If it matters for you then compare it with 9
                                cursorDirection = 'prev';
                            }
                            else if (key == 9) {
                                //if tab key is pressed then move next.
                                cursorDirection = 'next';
                            }
                            else {
                                cursorDirection == '';
                            }
                        });
                        

                        一旦有了光标方向,就可以使用nextElementSibling.focuspreviousElementSibling.focus 方法

                        【讨论】:

                        • 不幸的是,兄弟顺序与 Tab 顺序无关,除非是巧合,而且不能保证 prev/next 兄弟甚至可以聚焦。
                        猜你喜欢
                        • 1970-01-01
                        • 2021-11-15
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-07-19
                        • 1970-01-01
                        • 2018-05-04
                        • 2014-05-06
                        相关资源
                        最近更新 更多