Enfocar el siguiente elemento en el índice de la pestaña

103

Estoy tratando de mover el foco al siguiente elemento en la secuencia de pestañas según el elemento actual que tiene el foco. Hasta ahora no he encontrado nada en mis búsquedas.

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

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

Por supuesto, nextElementByTabIndex es la parte clave para que esto funcione. ¿Cómo encuentro el siguiente elemento en la secuencia de pestañas? La solución debería basarse en JScript y no en algo como JQuery.

JadziaMD
fuente
3
¿Por qué tienes esta línea currentElementId = "";?
1
No creo que ningún navegador exponga la información del orden de pestañas, y el algoritmo utilizado por los propios navegadores es demasiado complicado de replicar. Tal vez se puede restringir sus requisitos, por ejemplo, "sólo tienen en cuenta input, buttony textarealas etiquetas y hacer caso omiso tabindexde atributos".
Wladimir Palant
Necesitamos ver su .newElementByTabIndexcódigo porque eso es lo que no funciona.
0x499602D2
2
Por otra parte, tal vez la restricción a etiquetas particulares no sea necesaria; se puede verificar si el focus()método existe.
Wladimir Palant
1
@David Esa es la función que no existe, por eso mi pregunta. : D
JadziaMD

Respuestas:

23

Sin jquery: En primer lugar, en sus elementos tabulables, agregue class="tabable"esto y nos permitirá seleccionarlos más tarde. (No olvide el prefijo del selector de clase "." En el código siguiente)

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
fuente
16
Una solución sin tener que agregar un nombre a cada elemento (ya que hay muchos para hacer si es factible) sería ideal.
JadziaMD
3
ok, esto es para un formulario? Si todos los elementos que desea son los elementos de entrada, puede reemplazar la línea var tabbables = document.getElementsByName("tabable");con el var tabbables = document.getElementsByTagName("input");lugar
Brian Glaz
var tabbables = document.querySelectorAll("input, textarea, button")// IE8 +, obtenga una referencia a todas las tabulaciones sin modificar su HTML.
Greg
2
class = "tabbable" en lugar de usar el atributo de nombre
Chris F Carroll
4
Tenga en cuenta que al usar flexbox, el orden de los elementos es diferente en el DOM que visualmente en el navegador. Simplemente elegir el siguiente elemento tabbable no funciona cuando cambia el orden de los elementos usando flexbox.
Haneev
75

Nunca he implementado esto, pero he investigado un problema similar y esto es lo que intentaría.

Prueba esto primero

Primero, vería si pudiera simplemente disparar un keypressevento para la tecla Tab en el elemento que actualmente tiene el foco. Puede haber una forma diferente de hacer esto para diferentes navegadores.

Si eso no funciona, tendrá que trabajar más duro ...

Haciendo referencia a la implementación de jQuery, debe:

  1. Escuche Tab y Shift + Tab
  2. Sepa qué elementos se pueden tabular
  3. Comprender cómo funciona el orden de tabulación

1. Escuche Tab y Shift + Tab

Escuchar Tab y Shift + Tab probablemente estén bien cubiertos en otras partes de la web, por lo que omitiré esa parte.

2. Sepa qué elementos se pueden tabular

Saber qué elementos se pueden tabular es más complicado. Básicamente, un elemento se puede tabular si se puede enfocar y no tiene el atributo tabindex="-1"establecido. Entonces debemos preguntarnos qué elementos son enfocables. Los siguientes elementos son enfocables:

  • input, select, textarea, button, Y objectelementos que no estén deshabilitadas.
  • ay areaelementos que tienen hrefo tienen un valor numérico para el tabindexconjunto.
  • cualquier elemento que tenga un valor numérico para tabindexset.

Además, un elemento solo se puede enfocar si:

  • Ninguno de sus antepasados ​​lo es display: none.
  • El valor calculado de visibilityes visible. Esto significa que el ancestro más cercano que se haya visibilityestablecido debe tener un valor de visible. Si no se ha visibilityestablecido ningún antepasado , el valor calculado es visible.

Más detalles están en otra respuesta de Stack Overflow .

3. Comprender cómo funciona el orden de tabulación

El orden de tabulación de los elementos de un documento está controlado por el tabindexatributo. Si no se establece ningún valor, el tabindexes efectivamente 0.

El tabindexorden del documento es: 1, 2, 3,…, 0.

Inicialmente, cuando el bodyelemento (o ningún elemento) tiene el foco, el primer elemento en el orden de tabulación es el menor distinto de cero tabindex. Si varios elementos tienen lo mismo tabindex, siga el orden del documento hasta que llegue al último elemento con eso tabindex. Luego pasa al siguiente nivel más bajo tabindexy el proceso continúa. Finalmente, termine con esos elementos con un cero (o vacío) tabindex.

Chris Calo
fuente
37

Aquí hay algo que construí para este propósito:

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

caracteristicas:

  • conjunto configurable de elementos enfocables
  • no se necesita jQuery
  • funciona en todos los navegadores modernos
  • rápido y ligero
Mx.
fuente
2
Ésta es la solución más eficaz y económica. ¡Gracias! Aquí está mi script de trabajo completo: stackoverflow.com/a/40686327/1589669
eapo
Agregué un fragmento a continuación para incluir la clasificación por TabIndex explícito focussable.sort (sort_by_TabIndex)
DavB.cs
1
El mejor ! Se debe ser tan complejo: la nextElementSiblingpuede haber no enfocable, el siguiente enfocable no puede ser un hermano.
Tinmarino
Buen enfoque, pero debe permitir cualquier entrada que no sea de tipo hiddeny también cubrir textareay select.
Lucero
23

He creado un sencillo plugin de jQuery , que hace precisamente esto. Utiliza el selector ': tabbable' de jQuery UI para encontrar el siguiente elemento 'tabbable' y lo selecciona.

Uso de ejemplo:

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

El núcleo de la respuesta radica en encontrar el siguiente elemento:

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

Uso:

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

Observe que no me importa priorizar tabIndex.

André Werlang
fuente
3
¿Qué pasa si el orden tabindex va en contra del orden del documento? Creo que la matriz debe ordenarse por número de tabindex y luego por orden de documento
Chris F Carroll
Sí, eso sería más "compatible con las especificaciones". No estoy seguro acerca de los casos
extremos
¿Qué pasa si un artículo que no es una de esas etiquetas tiene un atributo tabindex?
Matt Pennington
1
@MattPennington Sería ignorado. El filtro es (un intento) de acelerar la búsqueda, no dude en adaptarse.
André Werlang
3

Como se mencionó en un comentario anterior, no creo que ningún navegador exponga información de orden de pestaña. Aquí una aproximación simplificada de lo que hace el navegador para obtener el siguiente elemento en orden de tabulación:

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

Esto solo considera algunas etiquetas e ignora los tabindexatributos, pero podría ser suficiente dependiendo de lo que esté tratando de lograr.

Wladimir Palant
fuente
3

Parece que puede verificar la tabIndexpropiedad de un elemento para determinar si es enfocable. Un elemento que no se puede enfocar tiene un valor tabindexde "-1".

Entonces solo necesita conocer las reglas para las tabulaciones:

  • tabIndex="1" tiene la mayor prioridad.
  • tabIndex="2" tiene la siguiente prioridad más alta.
  • tabIndex="3" es el siguiente, y así sucesivamente.
  • tabIndex="0" (o tabulado por defecto) tiene la prioridad más baja.
  • tabIndex="-1" (o no tabable de forma predeterminada) no actúa como una tabulación.
  • Para dos elementos que tienen el mismo tabIndex, el que aparece primero en el DOM tiene la prioridad más alta.

A continuación, se muestra un ejemplo de cómo crear la lista de tabulaciones, en secuencia, utilizando 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]);
}

Primero recorremos el DOM, recopilando todas las tabulaciones en secuencia con su índice. Luego armamos la lista final. Observe que agregamos los elementos con tabIndex="0"al final de la lista, después de los elementos con tabIndex1, 2, 3, etc.

Para ver un ejemplo completamente funcional, donde puede usar la tecla "Enter", consulte este violín .

chowey
fuente
2

Tabbable es un pequeño paquete JS que le brinda una lista de todos los elementos tabbables en orden de tabulación . Para que pueda encontrar su elemento dentro de esa lista, luego concentrarse en la siguiente entrada de la lista.

El paquete maneja correctamente los complicados casos extremos mencionados en otras respuestas (por ejemplo, ningún antepasado puede serlo display: none). ¡Y no depende de jQuery!

Al momento de escribir estas líneas (versión 1.1.1), tiene las advertencias de que no es compatible con IE8 y que los errores del navegador impiden que se maneje contenteditablecorrectamente.

Nate Sullivan
fuente
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
fuente
1

Esta es mi primera publicación en SO, así que no tengo suficiente reputación para comentar la respuesta aceptada, pero tuve que modificar el código a lo siguiente:

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

El cambio de var a constante no es crítico. El cambio principal es que nos deshacemos del selector que comprueba tabindex! = "-1". Luego, más tarde, si el elemento tiene el atributo tabindex Y está configurado en "-1", NO lo consideramos enfocable.

La razón por la que necesitaba cambiar esto fue porque al agregar tabindex = "- 1" a un <input>, este elemento todavía se consideraba enfocable porque coincide con el selector "input [type = text]: not ([disabled])". Mi cambio es equivalente a "si somos una entrada de texto no deshabilitada, y tenemos un atributo tabIndex, y el valor de ese atributo es -1, entonces no deberíamos ser considerados enfocables.

Creo que cuando el autor de la respuesta aceptada editó su respuesta para tener en cuenta el atributo tabIndex, no lo hizo correctamente. Por favor avíseme si este no es el caso

BrushyAmoeba
fuente
1

Existe la propiedad tabindex que se puede establecer en el componente. Especifica en qué orden se deben iterar los componentes de entrada al seleccionar uno y presionar tabulador. Los valores superiores a 0 están reservados para la navegación personalizada, 0 es "en orden natural" (por lo que se comportaría de manera diferente si se configura para el primer elemento), -1 significa que no se puede enfocar con el teclado:

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

También se puede configurar para algo más que los campos de entrada de texto, pero no es muy obvio lo que haría allí, en todo caso. Incluso si la navegación funciona, tal vez sea mejor usar "orden natural" para cualquier otra cosa que los elementos de entrada del usuario muy obvios.

No, no necesita JQuery ni ningún script para admitir esta ruta de navegación personalizada. Puede implementarlo en el lado del servidor sin ningún soporte de JavaScript. Por otro lado, la propiedad también funciona bien en el marco de React, pero no la requiere.

Audrius Meskauskas
fuente
0

Aquí hay una versión más completa de enfocarse en el siguiente elemento. Sigue las pautas de las especificaciones y ordena la lista de elementos correctamente mediante tabindex. Además, se define una variable inversa si desea obtener el elemento anterior.

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
fuente
0

Esta es una mejora potencial de la gran solución que ofrecieron @Kano y @Mx . Si desea conservar el orden de TabIndex, agregue este orden en el medio:

// 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
fuente
0

Puedes llamar a esto:

Lengüeta:

$.tabNext();

Mayús + Tabulador:

$.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 para completa.

tenía 169
fuente
Duplicado de esta respuesta , que fue publicada por el creador de ese complemento jQuery.
mbomb007
0

Nigromante.
Tengo un montón de 0-tabIndexes, que quería navegar por teclado.
Como en ese caso, solo importaba el ORDEN de los elementos, lo hice usandodocument.createTreeWalker

Entonces, primero crea el filtro (solo desea elementos [visibles], que tienen un atributo "tabIndex" con un valor NUMÉRICO.

Luego configura el nodo raíz, más allá del cual no desea buscar. En mi caso, this.m_treees un elemento ul que contiene un árbol toggable. Si desea el documento completo en su lugar, simplemente reemplácelo this.m_treecondocument.documentElement .

Luego establece el nodo actual en el elemento activo actual:

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

Luego regresa ni.nextNode()o ni.previousNode().

Nota:
esto NO devolverá las pestañas en el orden correcto si tiene tabIndices! = 0 y el orden de los elementos NO es el orden de tabIndex. En el caso de tabIndex = 0, tabOrder es siempre el orden de los elementos, por lo que esto funciona (en ese 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;
}

Tenga en cuenta que el 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
}

solo está ahí si lo desea si tiene criterios adicionales que no puede filtrar en el filtro pasado a createTreeWalker.

Tenga en cuenta que esto es TypeScript, debe eliminar todos los tokens detrás de dos puntos (:) y entre corchetes angulares (<>), por ejemplo <Element> o:(node: Node) => number para obtener JavaScript válido.

Aquí, como servicio, el JS transpilado:

"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
fuente
-1

¿Especificó sus propios valores de tabIndex para cada elemento que desea recorrer? si es así, puede intentar esto:

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
}

Estás usando jquery, ¿verdad?

Brian Glaz
fuente
No estamos usando JQuery ya que rompe la aplicación. : /
JadziaMD
Ok, creo que puedo reescribir sin usar jquery, dame un minuto
Brian Glaz
Todos los elementos que nos interesan tienen establecidos sus valores de índice de pestaña.
JadziaMD
-1

Revisé las soluciones anteriores y las encontré bastante largas. Se puede lograr con solo una línea de código:

currentElement.nextElementSibling.focus();

o

currentElement.previousElementSibling.focus();

aquí currentElement puede ser cualquier, es decir, document.activeElement o this si el elemento actual está en el contexto de la función.

Realicé un seguimiento de los eventos de tabulación y shift-tab con evento de teclado

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 vez que tenga la dirección del cursor, puede usar los métodos nextElementSibling.focusopreviousElementSibling.focus

Manpreet Singh Dhillon
fuente
1
Desafortunadamente, el orden de los hermanos no está relacionado con el orden de tabulación, excepto por una feliz coincidencia, y no hay garantía de que el hermano anterior / siguiente se pueda enfocar.
Lawrence Dol