Cómo usar tabindex

Cómo modificar el orden del DOM con tabindex

Dave Gash
Dave Gash
Meggin Kearney
Meggin Kearney

El orden de tabulación predeterminado que proporciona la posición del DOM de los elementos nativos es conveniente, pero hay ocasiones en las que querrás modificar el orden de tabulación, y mover elementos físicamente en el HTML no siempre es una solución óptima o viable. En estos casos, puedes usar el atributo HTML tabindex para establecer explícitamente la posición de pestaña de un elemento.

Navegadores compatibles

  • 1
  • 12
  • 1.5
  • ≤4

Origen

tabindex puede aplicarse a cualquier elemento, aunque no es necesariamente útil para todos los elementos, y admite un rango de valores de números enteros. Con tabindex, puedes especificar un orden explícito para los elementos de página enfocables, insertar un elemento que, de lo contrario, no se podría enfocar, en el orden de tabulación y quitar elementos de este. Por ejemplo:

tabindex="0": Inserta un elemento en el orden natural de tabulación. Para enfocar el elemento, presiona la tecla Tab, y llama a su método focus() para enfocarlo.

<custom-button tabindex="0">Press Tab to Focus Me!</custom-button>

Presiona Tab para enfocarme

tabindex="-1": Quita un elemento del orden natural de tabulación, pero aún se puede enfocar llamando a su método focus().

// TODO: DevSite - Code sample removed as it used inline event handlers

// TODO: DevSite - Se quitó la muestra de código porque usaba controladores de eventos intercalados

tabindex="5": Cualquier tabindex superior a 0 salta el elemento al principio del orden natural de tabulación. Si hay varios elementos con un tabindex superior a 0, el orden de tabulación comienza desde el valor más bajo que sea mayor que cero y avanza hacia arriba. El uso de un tabindex superior a 0 se considera un antipatrón.

<button>I should be first</button>
<button>And I should be second</button>
<button tabindex="5">But I jumped to the front!</button>

Esto es así especialmente para los elementos que no son de entrada, como encabezados, imágenes o títulos de artículos. Agregar tabindex a ese tipo de elementos es contraproductivo. Si es posible, te recomendamos que organices el código fuente para que la secuencia del DOM proporcione un orden de tabulación lógico. Si usas tabindex, restríngelo a controles interactivos personalizados, como botones, pestañas, menús desplegables y campos de texto, es decir, elementos a los que el usuario podría esperar proporcionar entradas.

No te preocupes de que los usuarios de lectores de pantalla pierdan contenido importante porque no tienen un tabindex. Incluso si el contenido es muy importante, como una imagen, si no es algo con lo que el usuario pueda interactuar, no hay razones para que sea enfocable. Los usuarios de lectores de pantalla pueden comprender el contenido de la imagen, siempre y cuando proporciones la compatibilidad adecuada con el atributo alt, lo que abordaremos en breve.

Cómo administrar el enfoque a nivel de la página

Esta es una situación en la que tabindex no solo es útil, sino también necesario. Puedes compilar una sola página sólida con diferentes secciones de contenido, de las cuales no todas son visibles a la vez. En este tipo de página, hacer clic en un vínculo de navegación puede cambiar el contenido visible sin actualizar la página.

Cuando esto sucede, es probable que identifiques el área de contenido seleccionada, le asignes un tabindex de -1 para que no aparezca en el orden natural de tabulación y llames a su método focus. Esta técnica, llamada administración de enfoque, mantiene el contexto que percibe el usuario en sincronización con el contenido visual del sitio.

Cómo administrar el enfoque en los componentes

Es importante administrar el enfoque cuando cambias algo en la página, pero a veces necesitas administrar el enfoque a nivel de control, por ejemplo, si compilas un componente personalizado.

Considera el elemento select nativo. Puede recibir el enfoque básico, pero, una vez allí, puedes usar las teclas de flecha para mostrar funcionalidades adicionales (las opciones seleccionables). Si estuvieras compilando un elemento select personalizado, te recomendamos que expongas estos mismos tipos de comportamientos para que los usuarios que utilizan principalmente el teclado puedan seguir interactuando con tu control.

<!-- Focus the element using Tab and use the up/down arrow keys to navigate -->
<select>
    <option>Aisle seat</option>
    <option>Window seat</option>
    <option>No preference</option>
</select>

Saber qué comportamientos del teclado implementar puede ser difícil, pero hay un documento útil que puedes consultar. La guía Prácticas de creación accesibles de aplicaciones de Internet enriquecidas (ARIA) enumera tipos de componentes y qué tipos de acciones del teclado admiten. Hablaremos sobre ARIA con más detalle más adelante, pero, por ahora, usaremos la guía para ayudarnos a agregar compatibilidad con el teclado a un componente nuevo.

Quizás estés trabajando en algunos elementos personalizados nuevos que se parecen a un conjunto de botones de selección, pero con tu visión única de la apariencia y el comportamiento.

<radio-group>
    <radio-button>Water</radio-button>
    <radio-button>Coffee</radio-button>
    <radio-button>Tea</radio-button>
    <radio-button>Cola</radio-button>
    <radio-button>Ginger Ale</radio-button>
</radio-group>

Para determinar qué tipo de compatibilidad con el teclado necesitan, consulta la guía de prácticas para creación de ARIA. La sección 2 contiene una lista de patrones de diseño que incluye una tabla de características para los grupos de botones de selección, el componente existente que más coincide con tu elemento nuevo.

Como puedes ver en la tabla, uno de los comportamientos comunes del teclado que se debería admitir son las teclas de flecha hacia arriba/abajo/izquierda/derecha. Para agregar este comportamiento al componente nuevo, usaremos una técnica llamada tabindex itinerante.

Extracto de especificaciones del W3C para los botones de selección

El tabindex itinerante funciona estableciendo tabindex en -1 para todos los elementos secundarios, excepto el que está activo en la actualidad.

<radio-group>
    <radio-button tabindex="0">Water</radio-button>
    <radio-button tabindex="-1">Coffee</radio-button>
    <radio-button tabindex="-1">Tea</radio-button>
    <radio-button tabindex="-1">Cola</radio-button>
    <radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>

Luego, el componente usa un objeto de escucha de eventos del teclado para determinar qué tecla presiona el usuario. Cuando esto sucede, establece el tabindex del elemento secundario enfocado anteriormente en -1, el tabindex del elemento secundario que se enfocará en 0 y llama al método de enfoque en él.

<radio-group>
    // Assuming the user pressed the down arrow, we'll focus the next available child
    <radio-button tabindex="-1">Water</radio-button>
    <radio-button tabindex="0">Coffee</radio-button> // call .focus() on this element
    <radio-button tabindex="-1">Tea</radio-button>
    <radio-button tabindex="-1">Cola</radio-button>
    <radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>

Cuando el usuario llegue al último elemento secundario (o el primero, según la dirección en la que mueva el foco), harás un bucle y volverás a enfocar el primer elemento secundario (o el último).

A continuación, puedes probar el ejemplo completo. Inspecciona el elemento en Herramientas para desarrolladores para observar el desplazamiento del índice de tabulación de un botón de selección al siguiente.

Agua Café Cola Ginger Ale

// TODO: DevSite - Se quitó la muestra de código porque usaba controladores de eventos intercalados

Puedes ver la fuente completa de este elemento en GitHub.

Trampas modales y teclados

A veces, cuando administras el enfoque, puedes entrar en una situación de la que no puedes salir. Considera un widget de autocompletado que intente administrar el enfoque y capture el comportamiento de la pestaña, pero evita que el usuario lo abandone hasta que se complete. Esto se conoce como tecla trampa y puede ser muy frustrante para el usuario. La sección 2.1.2 de la lista de tareas de AIM web aborda este problema, ya que indica que el enfoque del teclado nunca debe bloquearse ni retenerse en un elemento de la página en particular. El usuario debe poder navegar hacia y desde todos los elementos de la página usando solo el teclado.

Curiosamente, hay momentos en los que este comportamiento es realmente conveniente, como en una ventana modal. Por lo general, cuando se muestra la ventana modal, no quieres que el usuario acceda al contenido detrás de ella. Puedes agregar una superposición para cubrir visualmente la página, pero eso no evita que el foco del teclado se dirija accidentalmente fuera de la ventana modal.

Una ventana modal que le solicita al usuario que guarde su trabajo.

En instancias como esta, puedes implementar una trampa de teclado temporal para asegurarte de atrapar el enfoque solo mientras se muestra la ventana modal y, luego, restablecer el enfoque en el elemento enfocado anteriormente cuando se cierre la ventana modal.

Hay algunas propuestas sobre cómo facilitar esto a los desarrolladores, incluido el elemento <dialog>, pero aún no tienen una compatibilidad generalizada con los navegadores.

Consulta este artículo de MDN para obtener más información sobre <dialog> y este ejemplo modal si deseas obtener más información sobre las ventanas modales.

Considera un diálogo modal representado por una div que contenga algunos elementos y otro div que represente una superposición de fondo. Veamos los pasos básicos necesarios para implementar una trampa de teclado temporal en esta situación.

  1. Con document.querySelector, selecciona los divs modales y de superposición, y almacena sus referencias.
  2. Cuando se abre la ventana modal, almacena una referencia al elemento que se enfocó cuando se abrió la ventana modal para que puedas mostrar el enfoque en ese elemento.
  3. Usa un objeto de escucha de keydown para tomar teclas cuando se presionan mientras la ventana modal está abierta. También puedes detectar un clic en la superposición de fondo y cerrar la ventana modal si el usuario hace clic en ella.
  4. A continuación, obtén la colección de elementos enfocables dentro de la ventana modal. El primero y el último elemento enfocable actuarán como "centinelas" para avisarte cuándo hacer un bucle de enfoque hacia delante o hacia atrás para permanecer dentro de la ventana modal.
  5. Muestra la ventana modal y enfoca el primer elemento enfocable.
  6. A medida que el usuario presione Tab o Shift+Tab, mueve el enfoque hacia adelante o hacia atrás, haciendo un bucle en el último o el primer elemento según corresponda.
  7. Si el usuario presiona Esc, cierra la ventana modal. Esto es muy útil porque le permite al usuario cerrar la ventana modal sin buscar un botón de cierre específico, y beneficia incluso a los usuarios que usan un mouse.
  8. Cuando se cierre la ventana modal, ocúltala junto con la superposición de fondo y restablece el enfoque en el elemento que se guardó anteriormente.

Este procedimiento te brinda una ventana modal utilizable y no frustrante que todos pueden usar de manera efectiva.

Para obtener más detalles, puedes examinar este código de muestra y ver un ejemplo real de una página completada.