J'essaie de déplacer le focus sur l'élément suivant de la séquence de tabulation en fonction de l'élément actuel qui a le focus. Jusqu'ici, je n'ai rien trouvé dans mes recherches.
function OnFocusOut()
{
var currentElement = $get(currentElementId); // ID set by OnFocusIn
currentElementId = "";
currentElement.nextElementByTabIndex.focus();
}
Bien entendu, l'élément nextElementByTabIndex est l'élément clé pour que cela fonctionne. Comment trouver le prochain élément de la séquence de tabulation? La solution devrait être basée sur JScript et non sur JQuery.
Sans jquery: Tout d’abord, sur vos éléments tabulables, ajoutez class="tabable"
pour nous permettre de les sélectionner plus tard . (N'oubliez pas le préfixe de sélecteur de classe "." Dans le code ci-dessous)
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;
}
}
}
Je n'ai jamais mis cela en œuvre, mais j'ai étudié un problème similaire et voici ce que je voudrais essayer.
Premièrement, je verrais si vous pouviez simplement déclencher un événement keypress
pour la touche de tabulation de l’élément ayant le focus. Il peut y avoir une manière différente de faire cela pour différents navigateurs.
En référençant l'implémentation de jQuery, vous devez:
L'écoute de Tab et Shift + Tab est probablement bien couverte ailleurs sur le Web, je vais donc sauter cette partie.
Savoir quels éléments sont tabulables est plus délicat. Fondamentalement, un élément peut être mis en tabulation s'il est focusable et que l'attribut tabindex="-1"
n'est pas défini. Nous devons donc nous demander quels éléments peuvent être mis au point. Les éléments suivants sont focalisables:
input
, select
, textarea
, button
et object
qui ne sont pas désactivés.a
et area
qui ont une valeur href
ou une valeur numérique pour tabindex
.tabindex
set.De plus, un élément est focalisable uniquement si:
display: none
.visibility
est visible
. Cela signifie que l'ancêtre le plus proche pour lequel visibility
est défini doit avoir la valeur visible
. Si aucun ancêtre n'a défini visibility
, alors la valeur calculée est visible
.Plus de détails sont dans un autre Stack Overflow answer .
L'ordre de tabulation des éléments d'un document est contrôlé par l'attribut tabindex
. Si aucune valeur n'est définie, la tabindex
est effectivement 0
.
L'ordre tabindex
pour le document est: 1, 2, 3,…, 0.
Initialement, lorsque l'élément body
(ou aucun élément) a le focus, le premier élément de l'ordre de tabulation est le plus faible tabindex
. Si plusieurs éléments ont la même tabindex
, vous passez ensuite dans l'ordre des documents jusqu'à atteindre le dernier élément avec cette tabindex
. Ensuite, vous passez à la variable tabindex
suivante la plus basse et le processus continue. Enfin, terminez avec ces éléments avec un zéro (ou un vide) tabindex
.
Voici quelque chose que je construis à cet effet:
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();
}
}
}
J'ai créé un simple plugin jQuery qui ne fait que cela. Il utilise le sélecteur ': tabbable' de jQuery UI pour rechercher le prochain élément 'tabbable' et le sélectionner.
Exemple d'utilisation:
// Simulate tab key when element is clicked
$('.myElement').bind('click', function(event){
$.tabNext();
return false;
});
Le cœur de la réponse consiste à trouver l'élément suivant:
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];
}
Usage:
var nextEl = findNextTabStop(element);
nextEl.focus();
Remarquez que je me fiche de donner la priorité à tabIndex
.
Comme mentionné dans un commentaire ci-dessus, je ne pense pas que les navigateurs exposent les informations de commande de tabulation. Voici une approximation simplifiée de ce que le navigateur fait pour obtenir le prochain élément dans l'ordre des tabulations:
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();
Cela ne prend en compte que certaines balises et ignore l'attribut tabindex
, mais peut être suffisant en fonction de ce que vous essayez d'atteindre.
Il semble que vous puissiez vérifier la propriété tabIndex
d'un élément pour déterminer s'il est focusable. Un élément non focusable a une tabindex
de "-1".
Ensuite, il vous suffit de connaître les règles relatives aux tabulations:
tabIndex="1"
a la plus haute priorité.tabIndex="2"
a la priorité la plus haute suivante.tabIndex="3"
est le suivant, et ainsi de suite.tabIndex="0"
(ou tabbable par défaut) a la priorité la plus basse.tabIndex="-1"
(ou non tabbable par défaut) ne fait pas office de tabulation.Voici un exemple de création de la liste des tabulations, en séquence, à l'aide de Javascript pur:
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]);
}
Nous parcourons d’abord le DOM, en collectant tous les points de tabulation en séquence avec leur index. Nous assemblons ensuite la liste finale. Notez que nous ajoutons les éléments avec tabIndex="0"
à la toute fin de la liste, après les éléments avec une tabIndex
de 1, 2, 3, etc.
Pour un exemple pleinement fonctionnel, où vous pouvez naviguer avec la touche "Entrée", jetez un œil à ce violon .
Avez-vous spécifié vos propres valeurs tabIndex pour chaque élément que vous souhaitez parcourir? Si c'est le cas, vous pouvez essayer ceci:
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
}
Vous utilisez JQuery, non?
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();
};
Il s’agit là d’une amélioration potentielle de l’excellente solution offerte par @Kano et @Mx . Si vous souhaitez conserver l'ordre de TabIndex, ajoutez ce type au milieu:
// 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);
J'ai vérifié les solutions ci-dessus et les ai trouvées assez longues . Cela peut être accompli avec une seule ligne de code:
currentElement.nextElementSibling.focus();
ou
currentElement.previousElementSibling.focus();
ici, currentElement peut être n'importe quel document.activeElement ou ceci si l’élément current est dans le contexte de la fonction.
J'ai suivi des événements de tabulation et de décalage avec un événement keydown
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 == '';
}
});
une fois que vous avez la direction du curseur, vous pouvez utiliser les méthodes nextElementSibling.focus ou previousElementSibling.focus
Voici une version plus complète de l'accent sur l'élément suivant. Il suit les directives de spécification et trie correctement la liste des éléments à l'aide de tabindex. De plus, une variable inverse est définie si vous voulez obtenir l'élément précédent.
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]);
}
J'espère que c'est utile.
<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)" />
puis utilisez javascript simple
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;
}
}
}
}
Tabbable est un petit paquet JS qui vous donne une liste de tous les éléments tabbables dans l'ordre de tabulation. Vous pouvez donc trouver votre élément dans cette liste, puis vous concentrer sur l'entrée suivante de la liste.
Le package gère correctement les cas Edge complexes mentionnés dans d'autres réponses (par exemple, aucun ancêtre ne peut être display: none
). Et cela ne dépend pas de jQuery!
À la date de rédaction de ce document (version 1.1.1), il a été mis en garde de ne pas prendre en charge IE8 et que les bogues du navigateur l’empêchaient de gérer contenteditable
correctement.
Vous pouvez appeler ça:
Languette:
$.tabNext();
Maj + 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>
Je modifie jquery.tabbable PlugIn pour terminer.
Ceci est mon premier article sur SO, je n'ai donc pas assez de réputation pour commenter la réponse acceptée, mais je devais modifier le code comme suit:
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()
}
}
}
Le changement de var en constante est non critique. Le principal changement est que nous nous débarrassons du sélecteur qui vérifie tabindex! = "-1". Ensuite, si l'élément possède l'attribut tabindex ET qu'il est défini sur "-1", nous ne le considérons pas comme étant focalisable.
La raison pour laquelle j’avais besoin de changer cela était parce que lors de l’ajout de tabindex = "- 1" à un <input>
, cet élément était toujours considéré comme étant focalisable car il correspond au sélecteur "input [type = text]: not ([disabled])". Ma modification équivaut à "si nous sommes une entrée de texte non désactivée et que nous avons un attribut tabIndex et que la valeur de cet attribut est -1, nous ne devrions pas être considérés comme étant focalisables.
Je crois que lorsque l'auteur de la réponse acceptée a modifié sa réponse pour prendre en compte l'attribut tabIndex, il ne l'a pas fait correctement. S'il vous plaît laissez-moi savoir si ce n'est pas le cas