Mettre l'accent sur l'élément suivant dans l'index de l'onglet

103

J'essaie de déplacer le focus vers l'élément suivant dans la séquence d'onglets en fonction de l'élément actuel qui a le focus. Jusqu'à présent, je n'ai rien trouvé dans mes recherches.

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

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

Bien sûr, le nextElementByTabIndex est l'élément clé pour que cela fonctionne. Comment trouver l'élément suivant dans la séquence d'onglets? La solution devrait être basée sur JScript et non sur quelque chose comme JQuery.

JadziaMD
la source
3
pourquoi avez-vous cette ligne currentElementId = "";?
1
Je ne pense pas que les navigateurs exposent les informations d'ordre de tabulation - et l'algorithme utilisé par les navigateurs eux-mêmes est trop compliqué à reproduire. Peut-être pouvez-vous restreindre vos exigences, par exemple "considérer seulement input, buttonet textareabalises et ignorer l' tabindexattribut".
Wladimir Palant
Nous avons besoin de voir votre .newElementByTabIndexcode car cela ne fonctionne pas.
0x499602D2
2
Là encore, peut-être que la restriction à des balises particulières n'est pas nécessaire - on peut vérifier si la focus()méthode existe.
Wladimir Palant
1
@David C'est la fonction qui n'existe pas, donc ma question. : D
JadziaMD

Réponses:

24

Sans jquery: Tout d'abord, sur vos éléments tabulables, ajoutez class="tabable"ceci nous permettra de les sélectionner plus tard. (N'oubliez pas le préfixe du 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;
        }
    }
}
Brian Glaz
la source
16
Une solution sans avoir à ajouter un nom à chaque élément (car il y en a beaucoup à faire si possible) serait idéale.
JadziaMD
3
ok, est-ce pour un formulaire? Si tous les éléments souhaités sont des éléments d'entrée, vous pouvez remplacer la ligne var tabbables = document.getElementsByName("tabable");par à la var tabbables = document.getElementsByTagName("input");place
Brian Glaz
var tabbables = document.querySelectorAll("input, textarea, button")// IE8 +, obtenez une référence à tous les tabbables sans modifier votre HTML.
Greg
2
class = "tabbable" plutôt que d'utiliser l'attribut name
Chris F Carroll
4
Notez qu'en utilisant flexbox, l'ordre des éléments est différent dans le DOM que visuellement dans le navigateur. Le simple fait de choisir l'élément tabbable suivant ne fonctionne pas lorsque vous modifiez l'ordre des éléments à l'aide de flexbox.
Haneev
75

Je n'ai jamais implémenté cela, mais j'ai examiné un problème similaire, et voici ce que j'essaierais.

Essayez ceci en premier

Tout d'abord, je verrais si vous pouviez simplement déclencher un keypressévénement pour la touche Tab sur l'élément qui a actuellement le focus. Il peut y avoir une manière différente de faire cela pour différents navigateurs.

Si cela ne fonctionne pas, vous devrez travailler plus dur…

En référençant l'implémentation jQuery, vous devez:

  1. Écoutez Tab et Shift + Tab
  2. Sachez quels éléments peuvent être tabulés
  3. Comprendre le fonctionnement de l'ordre des tabulations

1. Écoutez Tab et Shift + Tab

L'écoute de Tab et Shift + Tab est probablement bien couverte ailleurs sur le Web, je vais donc sauter cette partie.

2. Sachez quels éléments peuvent être tabulés

Savoir quels éléments peuvent être tabulés est plus délicat. Fondamentalement, un élément peut être tabulé s'il est focusable et n'a pas l'attribut tabindex="-1"défini. Alors nous devons nous demander quels éléments sont focalisables. Les éléments suivants sont focalisables:

  • input, select, textarea, buttonEt des objectéléments qui ne sont pas désactivés.
  • aet les areaéléments qui ont une hrefvaleur numérique ou une valeur pour tabindexset.
  • tout élément qui a une valeur numérique pour tabindexset.

De plus, un élément n'est focalisable que si:

  • Aucun de ses ancêtres ne l'est display: none.
  • La valeur calculée de visibilityest visible. Cela signifie que l'ancêtre le plus proche à avoir visibilitydéfini doit avoir la valeur visible. Si aucun ancêtre n'est visibilitydéfini, la valeur calculée est visible.

Plus de détails sont dans une autre réponse Stack Overflow .

3. Comprendre le fonctionnement de l'ordre de tabulation

L'ordre de tabulation des éléments dans un document est contrôlé par l' tabindexattribut. Si aucune valeur n'est définie, le tabindexest effectivement 0.

L' tabindexordre du document est: 1, 2, 3,…, 0.

Initialement, lorsque l' bodyélément (ou aucun élément) a le focus, le premier élément dans l'ordre de tabulation est le plus petit différent de zéro tabindex. Si plusieurs éléments ont les mêmes éléments tabindex, vous passez dans l'ordre des documents jusqu'à ce que vous atteigniez le dernier élément avec cela tabindex. Ensuite, vous passez au niveau le plus bas suivant tabindexet le processus se poursuit. Enfin, terminez avec ces éléments avec un zéro (ou vide) tabindex.

Chris Calo
la source
37

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

Fonctionnalités:

  • ensemble configurable d'éléments focalisables
  • pas besoin de jQuery
  • fonctionne dans tous les navigateurs modernes
  • rapide et léger
Mx.
la source
2
C'est la solution la plus efficace et la plus respectueuse des ressources. Je vous remercie! Voici mon script de travail complet: stackoverflow.com/a/40686327/1589669
eapo
J'ai ajouté un extrait ci-dessous pour inclure le tri par TabIndex explicite focussable.sort (sort_by_TabIndex)
DavB.cs
1
Le meilleur ! Cela doit être aussi complexe: le nextElementSiblingfocusable peut ne pas être focusable, le focusable suivant peut ne pas être un frère ou une sœur.
Tinmarino
Bonne approche, mais elle devrait permettre toute entrée qui n'est pas de type hiddenet également couvrir textareaet select.
Lucero
23

J'ai créé un simple plugin jQuery qui fait exactement cela. Il utilise le sélecteur ': tabbable' de jQuery UI pour trouver l'élément 'tabbable' suivant et le sélectionne.

Exemple d'utilisation:

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

Le cœur de la réponse réside dans la recherche de 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 la priorité tabIndex.

André Werlang
la source
3
Que faire si l'ordre tabindex va à l'encontre de l'ordre des documents? Je pense que le tableau doit être trié par numéro de tabindex puis par ordre de document
Chris F Carroll
Oui, ce serait plus "conforme aux spécifications". Je ne suis pas sûr des cas limites, des éléments parents, etc.
André Werlang
Que faire si un élément qui ne fait pas partie de ces balises possède un attribut tabindex?
Matt Pennington
1
@MattPennington Ce serait ignoré. Le filtre est (une tentative) pour accélérer la recherche, n'hésitez pas à vous adapter.
André Werlang
3

Comme mentionné dans un commentaire ci-dessus, je ne pense pas que les navigateurs exposent des informations sur l'ordre des onglets. Voici une approximation simplifiée de ce que fait le navigateur pour obtenir l'élément suivant dans l'ordre de tabulation:

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 les tabindexattributs, mais cela peut suffire en fonction de ce que vous essayez de réaliser.

Wladimir Palant
la source
3

Il semble que vous puissiez vérifier la tabIndexpropriété d'un élément pour déterminer s'il est focalisable. Un élément qui n'est pas focalisable a un tabindex"-1".

Ensuite, il vous suffit de connaître les règles des taquets de tabulation:

  • tabIndex="1" a la priorité la plus élevée.
  • tabIndex="2" a la deuxième priorité la plus élevée.
  • 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) n'agit pas comme un taquet de tabulation.
  • Pour deux éléments qui ont le même tabIndex, celui qui apparaît en premier dans le DOM a la priorité la plus élevée.

Voici un exemple de construction de la liste des taquets de tabulation, en séquence, en utilisant du 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 rassemblant tous les arrêts 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 un tabIndexde 1, 2, 3, etc.

Pour un exemple pleinement fonctionnel, où vous pouvez naviguer à l'aide de la touche "Entrée", consultez ce violon .

Chowey
la source
2

Tabbable est un petit package JS qui vous donne une liste de tous les éléments tabables dans l'ordre de tabulation . Ainsi, vous pouvez trouver votre élément dans cette liste, puis vous concentrer sur l'entrée de liste suivante.

Le paquet gère correctement les cas extrêmes compliqués mentionnés dans d'autres réponses (par exemple, aucun ancêtre ne peut l'être display: none). Et cela ne dépend pas de jQuery!

Au moment d'écrire ces lignes (version 1.1.1), il a mis en garde qu'il ne prend pas en charge IE8 et que les bogues du navigateur l'empêchent de gérer contenteditablecorrectement.

Nate Sullivan
la source
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();
};
Dustin Poissant
la source
1

C'est mon premier article sur SO, donc je n'ai pas assez de réputation pour commenter la réponse acceptée, mais j'ai dû 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 constant n'est pas critique. Le principal changement est que nous nous débarrassons du sélecteur qui vérifie tabindex! = "-1". Ensuite, si l'élément a l'attribut tabindex ET qu'il est mis à "-1", nous ne le considérons PAS focussable.

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 focalisable car il correspond au sélecteur "input [type = text]: not ([disabled])". Ma modification est équivalente à "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, alors nous ne devrions pas être considérés comme focalisables.

Je crois que lorsque l'auteur de la réponse acceptée a modifié sa réponse pour tenir compte de 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

BrushyAmoeba
la source
1

Il existe la propriété tabindex qui peut être définie sur le composant. Il spécifie dans quel ordre les composants d'entrée doivent être itérés lors de la sélection d'un et en appuyant sur tab. Les valeurs supérieures à 0 sont réservées à la navigation personnalisée, 0 est "dans l'ordre naturel" (il se comporterait donc différemment s'il était défini pour le premier élément), -1 signifie que le clavier n'est pas focalisable:

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

Il peut également être défini pour autre chose que les champs de saisie de texte, mais ce qu'il ferait là-bas n'est pas très évident, voire rien du tout. Même si la navigation fonctionne, il vaut peut-être mieux utiliser «l'ordre naturel» pour autre chose que les éléments d'entrée utilisateur très évidents.

Non, vous n'avez pas besoin de JQuery ou de script du tout pour prendre en charge ce chemin de navigation personnalisé. Vous pouvez l'implémenter côté serveur sans aucun support JavaScript. De l'autre côté, la propriété fonctionne également très bien dans le framework React mais ne l'exige pas.

Audrius Meskauskas
la source
0

Voici une version plus complète de la mise au point sur l'élément suivant. Il suit les directives de spécification et trie correctement la liste des éléments en utilisant tabindex. Une variable inverse est également définie si vous souhaitez 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]);
}
svarlitskiy
la source
0

Il s'agit d'une amélioration potentielle de la grande solution proposée par @Kano et @Mx . Si vous souhaitez conserver l'ordre des TabIndex, ajoutez ce tri 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);
DavB.cs
la source
0

Vous pouvez appeler ceci:

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 le plug- in jquery.tabbable pour terminer.

iHad 169
la source
Duplicata de cette réponse , qui a été publiée par le créateur de ce plugin jQuery.
mbomb007 le
0

Nécromancie.
J'ai un buch de 0-tabIndexes, que je voulais naviguer au clavier.
Puisque dans ce cas, seul l'ORDRE des éléments comptait, je l'ai fait en utilisantdocument.createTreeWalker

Vous créez donc d'abord le filtre (vous ne voulez que des éléments [visibles], qui ont un attribut "tabIndex" avec une valeur NUMERIQUE.

Ensuite, vous définissez le nœud racine, au-delà duquel vous ne souhaitez pas rechercher. Dans mon cas, this.m_treeest un élément ul contenant un arbre à bascule. Si vous voulez plutôt le document entier, remplacez-le simplement this.m_treepar document.documentElement.

Ensuite, vous définissez le nœud actuel sur l'élément actif actuel:

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

Ensuite, vous revenez ni.nextNode()ou ni.previousNode().

Remarque:
cela ne renverra PAS les onglets dans le bon ordre si vous avez tabIndices! = 0 et que l'ordre des éléments n'est PAS l'ordre tabIndex. Dans le cas de tabIndex = 0, le tabOrder est toujours l'ordre des éléments, c'est pourquoi cela fonctionne (dans ce cas).

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

Notez que la boucle 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
}

n'est là que si vous le souhaitez si vous avez des critères supplémentaires que vous ne pouvez pas filtrer dans le filtre passé à createTreeWalker.

Notez qu'il s'agit de TypeScript, vous devez supprimer tous les jetons derrière les deux-points (:), et entre les crochets (<>), par exemple <Element>ou :(node: Node) => numberpour obtenir du JavaScript valide.

Ici en tant que service, le JS transpilé:

"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;
}
Stefan Steiger
la source
-1

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?

Brian Glaz
la source
Nous n'utilisons pas JQuery car il brise l'application. : /
JadziaMD
Ok, je pense que je peux réécrire sans utiliser jquery, donnez-moi une minute
Brian Glaz
Chaque élément qui nous intéresse a ses valeurs d'index de tabulation définies.
JadziaMD
-1

J'ai vérifié les solutions ci-dessus et je 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 ie document.activeElement ou ceci si l'élément courant est dans le contexte de la fonction.

J'ai suivi les événements d'onglet et de décalage de tabulation avec l'é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.focusoupreviousElementSibling.focus

Manpreet Singh Dhillon
la source
1
Malheureusement, l'ordre des frères et sœurs n'est pas lié à l'ordre des tabulations, sauf par coïncidence heureuse, et il n'y a aucune garantie que le frère précédent / suivant sera même focalisable.
Lawrence Dol