135

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

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

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

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

4

21 回答 21

87

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

先试试这个

首先,我想看看您是否可以简单地为当前具有焦点的元素上的 Tab 键触发一个keypress事件。对于不同的浏览器,可能有不同的方法来执行此操作。

如果这不起作用,您将不得不更加努力......</h1>

引用 jQuery 实现,您必须:

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

1. 听 Tab 和 Shift+Tab

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

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

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

  • input, select, textarea, button, 和object未被禁用的元素。
  • a以及area具有href或具有 set 数值的元素tabindex
  • 任何具有 set 数值的元素tabindex

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

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

更多详细信息在另一个Stack Overflow 答案中。

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

文档中元素的 Tab 键顺序由tabindex属性控制。如果未设置任何值,则tabindex是有效的0

文档的tabindex顺序是:1, 2, 3, ..., 0。

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

于 2012-05-24T02:21:55.517 回答
53

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

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
  • 适用于所有现代浏览器
  • 快速轻便
于 2016-02-03T09:45:47.167 回答
30

没有 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;
        }
    }
}
于 2011-08-26T17:51:06.287 回答
26

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

示例用法:

// Simulate tab key when element is clicked 
$('.myElement').bind('click', function(event){
    $.tabNext();
    return false;
});
于 2013-09-11T19:41:42.680 回答
11

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

  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

于 2015-04-26T20:32:07.970 回答
6

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

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

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

于 2017-11-23T06:51:39.977 回答
4

似乎您可以检查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"在列表的最末尾添加了带有tabIndex1、2、3 等的 a 的项目之后。

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

于 2013-11-13T02:40:48.967 回答
3

正如上面评论中提到的,我认为任何浏览器都不会公开标签顺序信息。下面是浏览器为按 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属性,但可能就足够了,具体取决于您要实现的目标。

于 2011-08-26T18:22:35.807 回答
2
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();
};
于 2019-04-23T19:03:11.443 回答
2

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

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

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

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

于 2020-04-20T16:15:00.703 回答
1

这是我在 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" 添加到 an<input>时,该元素仍然被认为是可聚焦的,因为它匹配 "input[type=text]:not([disabled])" 选择器。我的更改相当于“如果我们是非禁用文本输入,并且我们有一个 tabIndex 属性,并且该属性的值为 -1,那么我们不应该被认为是可聚焦的。

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

于 2017-11-28T00:22:37.187 回答
1

这是专注于下一个元素的更完整版本。它遵循规范指南并使用 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]);
}
于 2017-12-15T22:54:25.123 回答
0

这是对 @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);
于 2019-03-07T18:32:46.457 回答
0

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

因此,首先您创建过滤器(您只需要 [visible] 元素,这些元素的属性“tabIndex”具有 NUMERICAL 值。

然后设置根节点,您不想搜索超出该节点。在我的例子中,this.m_tree是一个包含可切换树的 ul 元素。如果您想要整个文档,只需替换this.m_treedocument.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,您需要删除冒号 (:) 后面和尖括号 (<>) 之间的所有标记,例如<Element>:(node: Node) => 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;
}
于 2020-07-22T14:16:17.747 回答
0

您只需要更改选项卡索引。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>

了解更多

于 2021-10-22T03:48:05.917 回答
0
function focusNext() {
  var query = '[tabindex]';
  if (document.activeElement) {
    var elements = [...document.querySelectorAll(query)]
    var index = elements.indexOf(document.activeElement);
    index++
    if (index == elements.length) index = 0
    elements[index].focus()
  }
}

如果要针对其他元素,可以修改query以细化元素。

于 2022-02-13T00:10:32.220 回答
-1

您是否为要循环的每个元素指定了自己的 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,对吗?

于 2011-08-26T17:33:19.087 回答
-1

如果你使用库“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 PlugIn 来完成。

于 2019-03-22T10:20:25.420 回答
-1

一个忠告:不要试图在选项卡事件期间控制焦点落在哪里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事件中控制选项卡索引或重置tabIndex状态focusout等可能会更好。

更多信息在这里

于 2020-09-18T22:38:00.717 回答
-2

我使用这个代码,它依赖于库“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');
    }
});
于 2020-09-06T12:38:20.557 回答
-3

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

currentElement.nextElementSibling.focus();

或者

currentElement.previousElementSibling.focus();

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

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

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.focusorpreviousElementSibling.focus方法

于 2019-01-10T23:39:29.580 回答