Evidenzia elemento successivo nell'indice della scheda


103

Sto cercando di spostare lo stato attivo sull'elemento successivo nella sequenza di tabulazione in base all'elemento corrente che ha lo stato attivo. Finora non ho trovato nulla nelle mie ricerche.

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

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

Ovviamente nextElementByTabIndex è la parte fondamentale affinché funzioni. Come trovo l'elemento successivo nella sequenza di tabulazione? La soluzione dovrebbe essere basata utilizzando JScript e non qualcosa come JQuery.


3
perché hai questa linea currentElementId = "";?

1
Non penso che nessun browser esponga le informazioni sull'ordine di tabulazione e l'algoritmo utilizzato dai browser stessi è troppo complicato da replicare. Forse puoi limitare i tuoi requisiti, ad esempio "considera solo input, buttone textareacontrassegna e ignora l' tabindexattributo".
Wladimir Palant

Abbiamo bisogno di vedere il tuo .newElementByTabIndexcodice perché è quello che non funziona.
0x499602D2

2
Poi di nuovo, forse la restrizione a particolari tag non è necessaria: è possibile verificare se il focus()metodo esiste.
Wladimir Palant

1
@David Questa è la funzione che non esiste, quindi la mia domanda. : D
JadziaMD

Risposte:


24

Senza jquery: prima di tutto, sui tuoi elementi tabulabili, aggiungi class="tabable"questo ci permetterà di selezionarli in seguito. (Non dimenticare il prefisso del selettore di classe "." Nel codice sottostante)

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

16
Una soluzione senza dover aggiungere un nome a ogni elemento (poiché ce ne sono molti per renderlo possibile) sarebbe l'ideale.
JadziaMD

3
ok, è questo per un modulo? Se tutti gli elementi che si desidera sono elementi di input, è possibile sostituire la linea var tabbables = document.getElementsByName("tabable");con il var tabbables = document.getElementsByTagName("input");posto
Brian Glaz

var tabbables = document.querySelectorAll("input, textarea, button")// IE8 +, ottieni un riferimento a tutte le tabelle senza modificare il tuo HTML.
Greg

2
class = "tabbable" invece di utilizzare l'attributo name
Chris F Carroll

4
Nota che usando flexbox l'ordine degli elementi è diverso nel DOM che visivamente nel browser. La semplice selezione dell'elemento tabbable successivo non funziona quando si modifica l'ordine degli elementi utilizzando flexbox.
Haneev

75

Non l'ho mai implementato, ma ho esaminato un problema simile ed ecco cosa proverei.

Prova prima questo

Innanzitutto, vedrei se è possibile semplicemente attivare un keypressevento per il tasto Tab sull'elemento attualmente attivo. Potrebbe esserci un modo diverso per eseguire questa operazione per browser diversi.

Se non funziona, dovrai lavorare di più ...

Facendo riferimento all'implementazione di jQuery, è necessario:

  1. Ascolta Tab e Maiusc + Tab
  2. Scopri quali elementi possono essere tabulati
  3. Comprendi come funziona l'ordine di tabulazione

1. Ascolta Tab e Maiusc + Tab

L'ascolto di Tab e Maiusc + Tab è probabilmente ben trattato altrove sul Web, quindi salterò quella parte.

2. Sapere quali elementi possono essere tabulati

Sapere quali elementi possono essere tabulati è più complicato. Fondamentalmente, un elemento può essere tabulato se è attivabile e non ha l'attributo tabindex="-1"impostato. Quindi dobbiamo chiederci quali elementi sono focalizzabili. I seguenti elementi sono attivabili:

  • input, select, textarea, button, E objectgli elementi che non sono disabilitati.
  • ae gli areaelementi che hanno hrefo hanno un valore numerico per tabindexset.
  • qualsiasi elemento che abbia un valore numerico per tabindexset.

Inoltre, un elemento è focalizzabile solo se:

  • Nessuno dei suoi antenati lo è display: none.
  • Il valore calcolato di visibilityè visible. Ciò significa che l'antenato più vicino da visibilityimpostare deve avere un valore di visible. Se nessun antenato è stato visibilityimpostato, il valore calcolato è visible.

Maggiori dettagli sono in un'altra risposta di Stack Overflow .

3. Comprendere come funziona l'ordine di tabulazione

L'ordine di tabulazione degli elementi in un documento è controllato dall'attributo tabindex. Se non è impostato alcun valore, tabindexè effettivamente 0.

L' tabindexordine per il documento è: 1, 2, 3,…, 0.

Inizialmente, quando l' bodyelemento (o nessun elemento) è attivo, il primo elemento nell'ordine di tabulazione è il più basso diverso da zero tabindex. Se più elementi hanno lo stesso tabindex, vai nell'ordine del documento fino a raggiungere l'ultimo elemento con quello tabindex. Quindi si passa al successivo più basso tabindexe il processo continua. Infine, termina con quegli elementi con uno zero (o vuoto) tabindex.


37

Ecco qualcosa che costruisco per questo scopo:

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

Caratteristiche:

  • insieme configurabile di elementi attivabili
  • non è necessario jQuery
  • funziona in tutti i browser moderni
  • veloce e leggero

2
Questa è la soluzione più efficace e rispettosa delle risorse. Grazie! Ecco il mio script piena efficienza: stackoverflow.com/a/40686327/1589669
dell'OESC

Ho aggiunto uno snippet di seguito per includere l'ordinamento in base a TabIndex esplicito focussable.sort (sort_by_TabIndex)
DavB.cs

1
Il migliore ! Si deve essere che complessa: la nextElementSiblingpotrebbe non essere attivabile, il prossimo attivabile non può essere un fratello.
Tinmarino

Buon approccio, ma dovrebbe consentire qualsiasi input che non sia di tipo hiddene anche coprire textareae select.
Lucero

23

Ho creato un semplice plugin jQuery che fa proprio questo. Utilizza il selettore ": tabbable" dell'interfaccia utente di jQuery per trovare l'elemento "tabbable" successivo e lo seleziona.

Utilizzo di esempio:

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

8

Il nucleo della risposta sta nel trovare l'elemento successivo:

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

Utilizzo:

var nextEl = findNextTabStop(element);
nextEl.focus();

Nota che non mi interessa stabilire le priorità tabIndex.


3
Cosa succede se l'ordine tabindex va contro l'ordine del documento? Penso che l'array debba essere ordinato per numero di tabindex, quindi per ordine del documento
Chris F Carroll

Sì, sarebbe più "conforme alle specifiche". Non sono sicuro dei casi limite, degli elementi genitore, ecc.
André Werlang

Cosa succede se un articolo che non è uno di questi tag ha un attributo tabindex?
Matt Pennington

1
@ MattPennington Sarebbe ignorato. Il filtro è (un tentativo) per velocizzare la ricerca, sentiti libero di adattarti.
André Werlang

3

Come accennato in un commento sopra, non credo che alcun browser esponga le informazioni sull'ordine di tabulazione. Ecco un'approssimazione semplificata di ciò che fa il browser per ottenere l'elemento successivo in ordine di tabulazione:

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

Questo considera solo alcuni tag e ignora l' tabindexattributo, ma potrebbe essere sufficiente a seconda di ciò che stai cercando di ottenere.


3

Sembra che tu possa controllare la tabIndexproprietà di un elemento per determinare se è attivabile. Un elemento che non è attivabile ha un valore tabindex"-1".

Quindi devi solo conoscere le regole per i punti di tabulazione:

  • tabIndex="1" ha la massima priorità.
  • tabIndex="2" ha la successiva priorità più alta.
  • tabIndex="3" è il prossimo, e così via.
  • tabIndex="0" (o tabbable per impostazione predefinita) ha la priorità più bassa.
  • tabIndex="-1" (o non tabbable per impostazione predefinita) non funge da tabulazione.
  • Per due elementi che hanno lo stesso tabIndex, quello che appare per primo nel DOM ha la priorità più alta.

Ecco un esempio di come costruire l'elenco dei punti di tabulazione, in sequenza, utilizzando Javascript puro:

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

Per prima cosa percorriamo il DOM, raccogliendo tutte le tabulazioni in sequenza con il loro indice. Quindi assembliamo l'elenco finale. Si noti che aggiungiamo gli elementi con tabIndex="0"alla fine dell'elenco, dopo gli elementi con atabIndex di 1, 2, 3, ecc.

Per un esempio completamente funzionante, dove puoi scorrere usando il tasto "invio", controlla questo violino .


2

Tabbable è un piccolo pacchetto JS che ti fornisce un elenco di tutti gli elementi tabbable in ordine di tabulazione . Quindi potresti trovare il tuo elemento all'interno di quell'elenco, quindi concentrarti sulla voce successiva dell'elenco.

Il pacchetto gestisce correttamente i complicati casi limite menzionati in altre risposte (ad esempio, nessun antenato può esserlo display: none). E non dipende da jQuery!

Al momento della stesura di questo documento (versione 1.1.1), ha l'avvertenza che non supporta IE8 e che i bug del browser gli impediscono di gestirlo contenteditablecorrettamente.


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

1

Questo è il mio primo post su SO, quindi non ho abbastanza reputazione per commentare la risposta accettata, ma ho dovuto modificare il codice come segue:

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

La modifica di var in constant non è critica. Il cambiamento principale è che ci sbarazziamo del selettore che controlla tabindex! = "-1". Successivamente, se l'elemento ha l'attributo tabindex E è impostato a "-1", NON lo consideriamo focalizzabile.

Il motivo per cui avevo bisogno di cambiare questo era perché quando si aggiungeva tabindex = "- 1" a un <input>, questo elemento era ancora considerato focalizzabile perché corrisponde al selettore "input [type = text]: not ([disabled])". La mia modifica è equivalente a "se siamo un input di testo non disabilitato e abbiamo un attributo tabIndex e il valore di quell'attributo è -1, allora non dovremmo essere considerati focalizzabili.

Credo che quando l'autore della risposta accettata ha modificato la sua risposta per tenere conto dell'attributo tabIndex, non l'ha fatto correttamente. Per favore fatemi sapere se questo non è il caso


1

C'è la proprietà tabindex che può essere impostata sul componente. Specifica in quale ordine devono essere iterati i componenti di input quando si seleziona uno e si preme tab. I valori superiori a 0 sono riservati per la navigazione personalizzata, 0 è "in ordine naturale" (quindi si comporterebbe diversamente se impostato per il primo elemento), -1 significa non attivabile da tastiera:

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

Può anche essere impostato per qualcos'altro oltre ai campi di immissione del testo, ma non è molto ovvio cosa farebbe lì, semmai. Anche se la navigazione funziona, forse è meglio usare "ordine naturale" per qualcosa di diverso dagli ovvi elementi di input dell'utente.

No, non hai bisogno di JQuery o di alcuno script per supportare questo percorso di navigazione personalizzato. Puoi implementarlo sul lato server senza alcun supporto JavaScript. Dall'altro lato, la proprietà funziona bene anche nel framework React ma non lo richiede.


0

Ecco una versione più completa del concentrarsi sull'elemento successivo. Segue le linee guida delle specifiche e ordina correttamente l'elenco degli elementi utilizzando tabindex. Inoltre, viene definita una variabile inversa se si desidera ottenere l'elemento precedente.

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

Questo è un potenziale miglioramento dell'ottima soluzione offerta da @Kano e @Mx . Se vuoi mantenere l'ordine TabIndex, aggiungi questo ordinamento nel mezzo:

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

0

Puoi chiamarlo:

Scheda:

$.tabNext();

Maiusc + 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>

Modifico jquery.tabbable PlugIn per completare.


Duplicato di questa risposta , che è stata pubblicata dal creatore di quel plugin jQuery.
mbomb007

0

Negromante.
Ho un sacco di 0-tabIndexes, che volevo navigare con la tastiera.
Poiché in quel caso contava solo l'ORDINE degli elementi, l'ho fatto usandodocument.createTreeWalker

Quindi per prima cosa crei il filtro (vuoi solo elementi [visibili], che hanno un attributo "tabIndex" con un valore NUMERICO.

Quindi imposti il ​​nodo radice, oltre il quale non vuoi cercare. Nel mio caso, this.m_treeè un elemento ul contenente un albero attivabile. Se invece desideri l'intero documento, sostituiscilo this.m_treecondocument.documentElement .

Quindi imposti il ​​nodo corrente sull'elemento attivo corrente:

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

Quindi torni ni.nextNode()o ni.previousNode().

Nota:
questo NON restituirà le tabulazioni nell'ordine corretto se hai tabIndices! = 0 e l'ordine degli elementi NON è l'ordine tabIndex. In caso di tabIndex = 0, tabOrder è sempre l'ordine degli elementi, motivo per cui funziona (in quel caso).

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

Nota che il ciclo 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
}

è lì solo se vuoi se hai criteri aggiuntivi che non puoi filtrare nel filtro passato a createTreeWalker.

Nota che questo è TypeScript, devi rimuovere tutti i token dietro i due punti (:) e tra parentesi angolari (<>), ad esempio <Element>o:(node: Node) => number per ottenere JavaScript valido.

Qui come servizio, il JS traspilato:

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

-1

Hai specificato i tuoi valori tabIndex per ogni elemento che desideri scorrere? in tal caso, puoi provare questo:

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
}

Stai usando jquery, giusto?


Non stiamo usando JQuery perché interrompe l'applicazione. : /
JadziaMD

Ok, penso di poter riscrivere senza usare jquery, dammi un minuto
Brian Glaz

Ogni elemento che ci interessa ha i propri valori di indice di tabulazione impostati.
JadziaMD

-1

Ho controllato le soluzioni sopra e le ho trovate piuttosto lunghe. Può essere realizzato con una sola riga di codice:

currentElement.nextElementSibling.focus();

o

currentElement.previousElementSibling.focus();

qui currentElement può essere qualsiasi, ad esempio, document.activeElement o this se l'elemento corrente è nel contesto della funzione.

Ho monitorato gli eventi di tabulazione e spostamento-tab con l'evento 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 == '';
    }
});

una volta che hai la direzione del cursore, puoi usare i metodi nextElementSibling.focusopreviousElementSibling.focus


1
Sfortunatamente l'ordine dei fratelli non è correlato all'ordine di tabulazione, tranne che per una felice coincidenza, e non c'è alcuna garanzia che il fratello precedente / successivo sarà anche focalizzabile.
Lawrence Dol
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.