javascript - Enfocar el siguiente elemento en el índice de pestañas
jscript.net (12)
Estoy intentando mover el foco al siguiente elemento en la secuencia de pestañas según el elemento actual que tiene 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, el 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 estar basada en JScript y no en algo como JQuery.
¿Especificó sus propios valores de tabIndex para cada elemento por el que desea pasar? si es así, puedes 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?
Aquí hay algo que construyo 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
Aquí hay una versión más completa de enfocarse en el siguiente elemento. Sigue las pautas de la especificación y ordena la lista de elementos correctamente mediante el uso de tabindex. También 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]);
}
Como mencioné en un comentario anterior, no creo que ningún navegador expone información sobre el orden de las pestañas. 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 el atributo tabindex
pero podría ser suficiente dependiendo de lo que esté tratando de lograr.
El núcleo de la respuesta está 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();
Tenga en cuenta que no me importa priorizar tabIndex
.
Espero que esto sea útil.
<input size="2" tabindex="1" id="one"
maxlength="2" onkeyup="toUnicode(this)" />
<input size="2" tabindex="2" id="two"
maxlength="2" onkeyup="toUnicode(this)" />
<input size="2" tabindex="3" id="three"
maxlength="2" onkeyup="toUnicode(this)" />
luego usa javascript simple
function toUnicode(elmnt)
{
var next;
if (elmnt.value.length==elmnt.maxLength)
{
next=elmnt.tabIndex + 1;
//look for the fields with the next tabIndex
var f = elmnt.form;
for (var i = 0; i < f.elements.length; i++)
{
if (next<=f.elements[i].tabIndex)
{
f.elements[i].focus();
break;
}
}
}
}
Esta es mi primera publicación en SO, por lo que no tengo la reputación suficiente 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 principal cambio es que nos deshacemos del selector que comprueba tabindex! = "-1". Luego, más adelante, si el elemento tiene el atributo tabindex Y está establecido en "-1", NO lo consideramos enfocable.
La razón por la que necesitaba cambiar esto era porque al agregar tabindex = "- 1" a una <input>
, este elemento todavía se consideraba enfocable porque coincide con el selector "input [type = text]: not ([disabled])". Mi cambio es equivalente a "si tenemos una entrada de texto no desactivada, y tenemos un atributo tabIndex, y el valor de ese atributo es -1, entonces no deberíamos considerarlo enfocable.
Creo que cuando el autor de la respuesta aceptada editó su respuesta para dar cuenta del atributo tabIndex, no lo hicieron correctamente. Por favor, avíseme si este no es el caso
Nunca lo he implementado, pero he investigado un problema similar, y esto es lo que probaría.
Prueba esto primero
Primero, vería si pudiera disparar un evento de keypress
para la tecla Tab en el elemento que actualmente tiene foco. Puede haber una forma diferente de hacer esto para diferentes navegadores.
Si eso no funciona, tendrás que trabajar más duro ...
Al hacer referencia a la implementación de jQuery, debes hacer lo siguiente:
- Escuche Tab y Mayús + Tabulador
- Sepa qué elementos son tabulables
- Comprender cómo funciona el orden de tabulación
1. Escuche Tab y Mayús + Tabulador
La escucha de Tab, Shift + Tab probablemente esté bien cubierta en otra parte de la web, así que omitiré esa parte.
2. Sepa qué elementos son tab-able
Saber qué elementos son tabulables es más complicado. Básicamente, un elemento puede tabindex="-1"
si es enfocable y no tiene el atributo tabindex="-1"
establecido. Entonces, debemos preguntarnos qué elementos son enfocables. Los siguientes elementos son enfocables:
-
input
,select
,textarea
,button
yobject
elements que no están deshabilitados. - y elementos de
area
que tienen unhref
o tienen un valor numérico para el conjuntotabindex
. - cualquier elemento que tenga un valor numérico para el conjunto
tabindex
.
Además, un elemento es enfocable solo si:
- Ninguno de sus antepasados tiene
display: none
. - El valor calculado de
visibility
esvisible
. Esto significa que el antecesor más cercano para tener el conjunto devisibility
debe tener un valor devisible
. Si ningún antepasado tienevisibility
establecida, entonces el valor calculado esvisible
.
Más detalles están en otra respuesta de desbordamiento de pila .
3. Comprender cómo funciona el orden de tabulación
El orden de tabulación de los elementos en un documento está controlado por el atributo tabindex
. Si no se establece ningún valor, el tabindex
es efectivamente 0
.
El orden tabindex
para el documento es: 1, 2, 3, ..., 0.
Inicialmente, cuando el elemento del body
(o ningún elemento) tiene foco, el primer elemento en el orden de tabulación es el tabindex
no nulo más tabindex
. Si los elementos múltiples tienen el mismo tabindex
, vaya en orden de documento hasta que llegue al último elemento con ese tabindex
. Luego pasas al siguiente tabindex
más tabindex
y el proceso continúa. Finalmente, termine con esos elementos con un tabindex
cero (o vacío).
Parece que puede verificar la propiedad tabIndex
de un elemento para determinar si es enfocable. Un elemento que no es tabindex
tiene un tabindex
de "-1".
Entonces solo necesita saber las reglas para las tabulaciones:
-
tabIndex="1"
tiene la prioridad más alta. -
tabIndex="2"
tiene la siguiente prioridad más alta. -
tabIndex="3"
es el siguiente, y así sucesivamente. -
tabIndex="0"
(o tabbable por defecto) tiene la prioridad más baja. -
tabIndex="-1"
(o no tabbable 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.
Aquí hay un ejemplo de cómo crear la lista de tabulaciones, en secuencia, usando 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 y recopilamos todas las tabulaciones en secuencia con su índice. Luego armamos la lista final. Tenga en cuenta que agregamos los elementos con tabIndex="0"
al final de la lista, después de los elementos con un tabIndex
de 1, 2, 3, etc.
Para un ejemplo totalmente funcional, donde puede buscar con la tecla "enter", revise este fiddle .
Sin jquery: Antes que nada, en sus elementos tab-able, agregue class="tabable"
esto nos permitirá seleccionarlos más tarde.
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;
}
}
}
Tabbable es un pequeño paquete JS que le proporciona una lista de todos los elementos tabulables en orden de tabulación . Así que podrías encontrar tu elemento dentro de esa lista, luego enfocarte en la siguiente entrada de la lista.
El paquete maneja correctamente los casos extremos complicados mencionados en otras respuestas (p. Ej., No se puede display: none
ningún ancestro display: none
). ¡Y no depende de jQuery!
A partir de este escrito (versión 1.1.1), tiene la advertencia de que no es compatible con IE8, y que los errores del navegador impiden que se maneje correctamente.
Creé un plugin jQuery simple que hace justamente esto. Utiliza el selector '': tabbable'' de jQuery UI para encontrar el siguiente elemento ''tabbable'' y lo selecciona.
Ejemplo de uso:
// Simulate tab key when element is clicked
$(''.myElement'').bind(''click'', function(event){
$.tabNext();
return false;
});