在标签索引中关注下一个元素

问题描述 投票:80回答:16

我试图根据具有焦点的当前元素将焦点移动到制表序列中的下一个元素。到目前为止,我的搜索中没有任何内容。

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

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

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

javascript jscript.net
16个回答
19
投票

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

1
投票

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

<input>

要么

currentElement.nextElementSibling.focus();

这里currentElement可以是任何ie document.activeElement,如果当前元素在函数的上下文中,则为this。

我用keydown事件跟踪了tab和shift-tab事件

currentElement.previousElementSibling.focus();

一旦你有光标方向,那么你可以使用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.focus方法


0
投票

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

previousElementSibling.focus

你正在使用jquery,对吧?


0
投票

希望这是有帮助的。

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
}

然后使用简单的JavaScript

<input size="2" tabindex="1" id="one"
  maxlength="2" onkeyup="toUnicode(this)" />

<input size="2" tabindex="2" id="two"
  maxlength="2" onkeyup="toUnicode(this)" />

<input size="2" tabindex="3" id="three"
 maxlength="2" onkeyup="toUnicode(this)" />

0
投票

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

function toUnicode(elmnt)
{
  var next;
 if (elmnt.value.length==elmnt.maxLength)
{
next=elmnt.tabIndex + 1;
//look for the fields with the next tabIndex
var f = elmnt.form;
for (var i = 0; i < f.elements.length; i++)
{
  if (next<=f.elements[i].tabIndex)
  {
    f.elements[i].focus();
    break;
    }
   }
  }
}

0
投票

这是对@Kano和@Mx提供的出色解决方案的潜在增强。如果要保留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]);
}

0
投票

你可以这样称呼:

标签:

// 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);

Shift + Tab键:

$.tabNext();

$.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> PlugIn来完成。


0
投票

60
投票

我从来没有实现过这个,但我已经研究过类似的问题了,这就是我想要的。

Try this first

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

If that doesn't work, you'll have to work harder…

引用jQuery实现,您必须:

  1. 听取Tab和Shift + Tab
  2. 知道哪些元素是可以选择的
  3. 了解Tab键顺序的工作原理

1.听取Tab和Shift + Tab

倾听Tab和Shift + Tab可能在网络上的其他地方都有很好的涵盖,所以我会跳过那一部分。

2.了解哪些元素可以选项

知道哪些元素是可制表的是棘手的。基本上,如果元素是可聚焦的并且没有设置tabindex="-1"属性,则该元素是可制表的。那么我们必须要问哪些要素是可集中的。以下元素是可聚焦的:

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

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

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

更多细节在另一个Stack Overflow answer

3.了解Tab键顺序的工作原理

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

该文件的tabindex订单是:1,2,3,...,0。

最初,当body元素(或没有元素)具有焦点时,Tab键顺序中的第一个元素是最低的非零tabindex。如果多个元素具有相同的tabindex,则按文档顺序进行,直到使用该tabindex到达最后一个元素。然后你移动到下一个最低的tabindex并继续进行。最后,使用零(或空)tabindex完成这些元素。


23
投票

这是我为此目的建立的东西:

focusNextElement: function () {
    //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
  • 适用于所有现代浏览器
  • 快速轻巧

21
投票

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

用法示例:

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

6
投票

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

  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的优先级。


2
投票

正如上面的评论所述,我认为任何浏览器都不会公开Tab键顺序信息。这里简化了浏览器以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属性,但可能足够取决于您要实现的目标。


1
投票

看来你可以检查一个元素的tabIndex属性来确定它是否可以聚焦。不可聚焦的元素具有“-1”的tabindex

然后你只需要知道制表位的规则:

  • qazxsw poi具有最高优先级。
  • tabIndex="1"具有次优先级。
  • tabIndex="2"是下一个,依此类推。
  • tabIndex="3"(默认为tabbable)具有最低优先级。
  • tabIndex="0"(默认情况下不是tabbable)不作为制表位。
  • 对于具有相同tabIndex的两个元素,DOM中首先出现的元素具有更高的优先级。

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

tabIndex="-1"

我们首先走DOM,按索引顺序收集所有制表位。然后我们组装最终列表。请注意,我们在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]); } 为1,2,3等的项目之后,在列表的最后添加tabIndex="0"的项目。

有关完整工作示例,您可以使用“输入”键进行选项卡,请查看此tabIndex


1
投票

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

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

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


1
投票

这是我关于SO的第一篇文章,所以我没有足够的声誉来评论接受的答案,但我必须将代码修改为以下内容:

contenteditable

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

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

我相信,当接受的答案的作者编辑了他们对tabIndex属性进行说明的答案时,他们没有正确地这样做。如果不是这样,请告诉我

© www.soinside.com 2019 - 2024. All rights reserved.