click-handler-keyboard-operable

Los manejadores de clic en elementos que no son controles nativos deben funcionar con el teclado

automaticWCAG 2.1.1, 2.1.3 (AAA)confianza: mediumgravedad: seriousoperable

Comprueba que un elemento que no es un control nativo y tiene un manejador de clic (un onclick en línea, o un listener de clic que vio el hook de listeners) puede recibir el foco del teclado y que un manejador de teclas en él, en un ancestro o en un descendiente recibe sus teclas, para que un usuario de teclado pueda ejecutar lo que ejecuta un clic.

Ámbito de aplicación. Elementos que la página renderiza con un atributo onclick en línea no vacío o, donde la página ejecuta el hook de listeners (listenerHookScript), con un listener de clic añadido desde un script (addEventListener('click', …) o la propiedad onclick) en el propio elemento, salvo:

  • <html> y <body>, donde un manejador de clic sirve a toda la página;
  • un manejador formado solo por instrucciones que no hacen nada (return false, void(0), void 0, event.preventDefault()): no ofrece ninguna función;
  • un elemento con aria-disabled="true", en sí mismo o en un ancestro: un control deshabilitado no ofrece ninguna función que operar;
  • los controles nativos, cuyo propio comportamiento de teclado llega al usuario: un enlace o area con href, button, input que no sea hidden, select, textarea, option, summary, label (activa su control, que recibe el foco por sí mismo), iframe, embed, audio o video con controls, y un anfitrión de edición (contenteditable);
  • un elemento que contiene, en el árbol plano (su shadow root y el contenido asignado a slots incluidos), un control cuya activación con el teclado dispara click (un enlace o area con href, button, summary, o un input de tipo button, submit, reset, image, checkbox o radio) que está en el orden de tabulación: el clic del control se propaga hasta el manejador, así que es delegación de eventos y el teclado ya lo ejecuta;
  • un elemento que no puede recibir el foco, no tiene ningún descendiente que pueda, no tiene rol de widget ni cursor: pointer: nada indica al usuario que se puede hacer clic en él, y un manejador así a menudo solo registra los clics o detiene su propagación.

Resultado esperado. Un usuario de teclado puede ejecutar lo que ejecuta el clic. Chromium dispara click con Intro o Espacio solo en los controles nativos: un <span role="button" tabindex="0" onclick> recibe el foco pero no hace nada con ninguna de las dos teclas.

  • El elemento, o uno de sus descendientes del árbol plano, está en el orden de tabulación, y hay un manejador de teclas en línea (onkeydown, onkeyup, onkeypress) en él, en un ancestro o en un descendiente, de modo que los eventos de teclado del elemento con el foco pueden llegar a uno: pass.
  • El elemento o un descendiente está en el orden de tabulación, pero no hay ningún manejador de teclas en línea: cantTell (CLICK_HANDLER_NO_KEY_HANDLER); compruebe que Intro y Espacio hacen lo mismo que un clic.
  • El elemento o un descendiente puede recibir el foco, pero solo con tabindex="-1", y ningún ancestro tiene un manejador de teclas: cantTell (CLICK_HANDLER_NOT_IN_TAB_ORDER). Tab nunca llega a él; un script puede mover el foco ahí, como hace un tabindex itinerante (roving tabindex).
  • Con el hook de listeners, un listener de teclado añadido desde un script en el elemento o en un descendiente cuenta como un manejador de teclas en línea. Uno que está solo en un ancestro, en un shadow root en el que está el elemento, en el documento o en la ventana puede gestionar las teclas del elemento o solo las de otros: cantTell (CLICK_HANDLER_KEY_LISTENER_ABOVE).
  • El elemento está en el orden de tabulación, su rol es button, link, checkbox, switch, tab, menuitem, menuitemcheckbox o menuitemradio, y un hook instalado antes de los scripts de la página no vio ningún listener de teclado en él, en un descendiente, en un ancestro, en un shadow root en el que está, en el documento ni en la ventana, ni hay ahí un manejador de teclas en línea: fail (CLICK_HANDLER_NO_KEY_LISTENER). Ningún evento de teclado llega a un manejador, así que Intro y Espacio no hacen nada. Salvo que haya un listener de focus o focusin en él, en un ancestro, en un shadow root en el que está, en el documento o en la ventana: puede añadir el listener de teclado una vez que el elemento tiene el foco o, en una pestaña, mostrar su panel al recibir el foco, así que cantTell (CLICK_HANDLER_FOCUS_LISTENER).
  • Ni él ni ningún descendiente puede recibir el foco, y su rol es button, link, checkbox o switch: fail (CLICK_HANDLER_CONTROL_NOT_FOCUSABLE). El elemento dice que es un control, y Tab nunca llega a él, como en el fallo F42 de WCAG para los enlaces generados por script. Salvo que un ancestro lleve aria-activedescendant o un manejador de teclas en línea, o sea un widget compuesto enfocable (grid, treegrid, listbox, menu, menubar, tree, tablist, toolbar, radiogroup, combobox), que puede operarlo sin moverle el foco: entonces cantTell.
  • Ni él ni ningún descendiente puede recibir el foco, con otro rol de widget o cursor: pointer: cantTell (CLICK_HANDLER_NOT_FOCUSABLE). Una opción de la lista de un combobox o una celda de un grid se suele operar desde el elemento que conserva el foco, y la acción puede ofrecerla otro control de la página.

Qué informa un hallazgo.

  • role: el rol ARIA del elemento, o '' si no tiene ninguno.
  • focusable: si el propio elemento puede recibir el foco.

Estos campos de data.details ayudan a leer y reproducir un hallazgo. Salvo reasonCode y los pocos campos que Esquema de salida indica como estables, no son un contrato estable: un campo puede cambiar de nombre o desaparecer en una versión menor, así que no construyas sobre ellos.

Ejemplos

pass
<span role="button" tabindex="0" onclick="save()" onkeydown="if (event.key === 'Enter' || event.key === ' ') save()">Save</span>

El span recibe el foco y su manejador de teclas ejecuta la misma acción que el clic.

fail
<div role="button" onclick="openPanel()">Open the panel</div>

El elemento dice que es un botón y ejecuta un manejador de clic, pero el foco del teclado nunca puede llegar a él.

can't tell
<span role="button" tabindex="0" onclick="showPreferences()">Manage my cookies</span>

El span recibe el foco, pero Intro y Espacio no disparan ningún clic en él y no tiene ningún manejador de teclas en línea; un listener añadido desde un script no se vería en el marcado.

Cómo comprobarlo

Cuando esta regla no puede decidir, informa cantTell y deja la pregunta a una persona. Cada pregunta que puede plantear aparece aquí bajo su código de motivo, con los pasos para responderla. Un hallazgo lleva los mismos pasos en howToCheck, en el idioma del análisis. Consulta Esquema de salida.

CLICK_HANDLER_FOCUS_LISTENER

  1. Hacer clic en el elemento que indica el informe y anotar lo que ocurre.
  2. Recargar la página y pulsar Tab hasta que el elemento tenga el foco.
  3. Pulsar Intro y luego Espacio, y compararlo con lo que hizo el clic.
  4. Si aquí no se puede hacer con el teclado, comprobar si lo que hace el clic se puede hacer de otra forma solo con el teclado, por ejemplo con un botón o una opción de menú en otra parte de la página. Solo cuenta si hace todo lo que hace el clic.

Se cumple. Intro (y Espacio en controles tipo botón) hace lo mismo que un clic, o, en una pestaña, mover el foco a ella ya muestra su panel, o lo que hace el clic se puede hacer de otra forma solo con el teclado.

No se cumple. Pulsar Intro o Espacio no hace nada aunque llegue el foco, y lo que hace el clic no se puede hacer de otra forma con el teclado, lo que incumple WCAG 2.1.1 (F54).

CLICK_HANDLER_KEY_LISTENER_ABOVE

  1. Hacer clic en el elemento que indica el informe y anotar lo que ocurre.
  2. Pulsar Tab hasta que el elemento tenga el foco.
  3. Pulsar Intro y luego Espacio (y las teclas de flecha si está en un grupo), y compararlo con el clic.
  4. Si aquí no se puede hacer con el teclado, comprobar si lo que hace el clic se puede hacer de otra forma solo con el teclado, por ejemplo con un botón o una opción de menú en otra parte de la página. Solo cuenta si hace todo lo que hace el clic.

Se cumple. Intro (y Espacio en controles tipo botón) hace lo mismo que un clic, o lo que hace el clic se puede hacer de otra forma solo con el teclado.

No se cumple. Pulsar Intro o Espacio no hace nada en el elemento, así que el detector de arriba no lo gestiona, y lo que hace el clic no se puede hacer de otra forma con el teclado, lo que incumple WCAG 2.1.1.

CLICK_HANDLER_NOT_FOCUSABLE

  1. Hacer clic en el elemento que indica el informe y anotar lo que hace su clic.
  2. Recorrer la página con Tab y comprobar que el elemento nunca recibe el foco.
  3. Si es un elemento de una lista, una cuadrícula o un combobox, poner el foco en ese widget y usar las teclas de flecha para ver si el elemento se activa y si Intro ejecuta la acción.
  4. Si no, buscar en la página otro control que se pueda usar con el teclado y que haga lo mismo.

Se cumple. La misma acción se puede realizar con el teclado, mediante el widget que la gestiona o mediante otro control.

No se cumple. La acción solo se puede realizar con el ratón, lo que incumple WCAG 2.1.1 (F54, F42).

CLICK_HANDLER_NOT_IN_TAB_ORDER

  1. Hacer clic en el elemento que indica el informe y anotar lo que ocurre.
  2. Recorrer la página con Tab y comprobar si el foco llega alguna vez al elemento.
  3. Si forma parte de un grupo (pestañas, menú, barra de herramientas, lista), entrar en el grupo con Tab y usar las teclas de flecha para ver si llevan el foco hasta él.
  4. Cuando tenga el foco, pulsar Intro o Espacio y compararlo con el clic.
  5. Si aquí no se puede hacer con el teclado, comprobar si lo que hace el clic se puede hacer de otra forma solo con el teclado, por ejemplo con un botón o una opción de menú en otra parte de la página. Solo cuenta si hace todo lo que hace el clic.

Se cumple. Se puede llegar al elemento con el teclado (Tab o teclas de flecha dentro de su grupo) e Intro o Espacio hace lo mismo que un clic, o lo que hace el clic se puede hacer de otra forma solo con el teclado.

No se cumple. Ninguna tecla lleva el foco al elemento y lo que hace el clic no se puede hacer de otra forma con el teclado, así que su acción solo funciona con ratón, lo que incumple WCAG 2.1.1.

CLICK_HANDLER_NO_KEY_HANDLER

  1. Hacer clic en el elemento que indica el informe y anotar lo que ocurre.
  2. Recargar la página y pulsar Tab hasta que el elemento tenga el foco.
  3. Pulsar Intro y luego Espacio, y compararlo con lo que hizo el clic.
  4. Si aquí no se puede hacer con el teclado, comprobar si lo que hace el clic se puede hacer de otra forma solo con el teclado, por ejemplo con un botón o una opción de menú en otra parte de la página. Solo cuenta si hace todo lo que hace el clic.

Se cumple. Intro (y Espacio en controles tipo botón) hace lo mismo que un clic, o lo que hace el clic se puede hacer de otra forma solo con el teclado.

No se cumple. Pulsar Intro o Espacio no hace nada, o menos que un clic, y lo que hace el clic no se puede hacer de otra forma con el teclado, lo que incumple WCAG 2.1.1 (F54).

Ver código fuente