选项卡索引中的下一个元素

我试图移动焦点到下一个元素的制表符序列的基础上,目前的元素有焦点。到目前为止,我还没有找到任何东西。

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


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

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

337920 次浏览

是否为要循环访问的每个元素指定了自己的 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: 首先,在可选项卡的元素上添加 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 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属性,但可能足够取决于您试图实现的内容。

我从来没有实现过这个,但是我研究过类似的问题,下面是我将尝试的方法。

参考 jQuery 实现,您必须:

  1. 搜索 Tab 和 Shift + Tab
  2. 知道哪些元素是可选项卡式的
  3. 了解制表顺序是如何工作的

听 Tab 和 Shift + Tab

搜索 Tab 和 Shift + Tab 可能已经在网络上的其他地方得到了很好的覆盖,所以我将跳过这一部分。

2. 知道哪些元素可以标签化

要知道哪些元素是可选项卡式的则更加困难。基本上,如果一个元素是可定焦的,并且没有设置属性 tabindex="-1",那么它是可制表符的。因此,我们必须询问哪些元素是可聚焦的。下列要素可以集中注意力:

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

此外,一个元素只有在下列情况下才可调焦:

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

更多细节在另一个 堆栈溢出答案中。

3. 了解标签顺序是如何工作的

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

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

最初,当 body元素(或没有元素)有焦点时,制表符顺序中的第一个元素是最低的非零 tabindex。如果多个元素具有相同的 tabindex,则按照文档顺序进行操作,直到使用该 tabindex到达最后一个元素。然后转移到下一个最低 tabindex,这个过程继续。最后,使用那些 tabindex为零(或空)的元素完成。

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

示例用法:

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

似乎可以检查元素的 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等的项之后。

对于一个完全工作的示例,您可以使用“ enter”键进行选项卡操作,请查看这个 小提琴

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

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

下面是我为此目的而建造的东西:

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
  • 适用于所有现代浏览器
  • 又快又轻

Tabtable 是一个小的 JS 包,它提供了所有表格元素 按标签顺序排列的列表。因此您可以在该列表中找到您的元素,然后关注下一个列表条目。

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

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

这是我的第一篇关于 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”,那么我们不认为它是可聚焦的。

我之所以需要改变这一点,是因为在向 <input>添加 tabindex = “-1”时,这个元素仍然被认为是可聚焦的,因为它匹配“ input [ type = text ] : not ([ disable ])”选择器。我的修改相当于“如果我们是一个非禁用的文本输入,并且我们有一个 tabIndex 属性,该属性的值为 -1,那么我们就不应该被认为是可聚焦的。

我认为,当接受答案的作者编辑他们的答案以说明 tabIndex 属性时,他们没有正确地这样做。如果不是这样的话,请告诉我

下面是关注下一个元素的更完整版本。它遵循规范指导原则,并使用 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]);
}

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

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

这是对 @ 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);

如果您使用库“ 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.tabtable插件来完成。

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

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

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

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

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

我有很多0-tabIndex,我想通过键盘来浏览它们。
因为在这种情况下,只有元素的 ORDER 是重要的,所以我使用 document.createTreeWalker来完成

因此,首先创建筛选器(您只需要[可见]元素,这些元素具有一个带有 NUMERICS 值的属性“ tabIndex”。

然后设置根节点,在该节点之外不要进行搜索。 在我的例子中,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,您需要删除冒号(:)后面和尖括号(< >)之间的所有标记,例如 <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;
}

我使用这段代码,它依赖于库“ 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');
}
});

一句忠告: 在制表符事件期间,不要试图控制焦点落在哪里。相反,尝试通过将 不要希望接收的元素的 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事件或 没有中控制 tab 索引来重置 focusout上的 tabIndex状态可能更好。

更多信息 给你

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

了解更多

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来细化元素。

最简单的 TAB循环!

// Give the focus onload. (The autofocus attribute does not work on div(s))
document.querySelector('[tabindex]').focus()


document.addEventListener("keydown", (e) => {
e.preventDefault() // Very important here
if (document.activeElement && e.code === "Tab") {
let e = [...document.querySelectorAll('[tabindex]')],
i = e.indexOf(document.activeElement) + 1;
i = i === e.length ? i = 0 : i;
e[i].focus()
}
})
<div tabindex="0">Try to</div>
<div tabindex="1">tap, or long press</div>
<div tabindex="2">the TAB key!</div>

这里是我的工作解决方案使用 tabbable库在我的反应应用程序,使’回车’行为像标签

handleKeyDown={(e) => {
if (e.key === 'Enter') {
const fElts = tabbable(document.documentElement);
const currEltIndex = fElts.findIndex(
(e) => e === document.activeElement,
);
const nextElt = fElts[currEltIndex + (e.shiftKey ? -1 : 1)];
nextElt?.focus();
}
}}