tabIndex no hace que una etiqueta sea enfocable usando la tecla Tabulador

12 minutos de lectura

avatar de usuario
hon2a

Estoy tratando de reemplazar las entradas de casilla de verificación/radio con íconos. Para esto, necesito ocultar la casilla de verificación/radio original. El problema es que también quiero que el formulario admita adecuadamente la entrada del teclado, es decir, deje que la entrada permanezca enfocable por Tab tecla y seleccionable mediante Spacebar. Dado que estoy ocultando la entrada, no se puede enfocar, así que en su lugar, estoy tratando de hacer que su <label> enfocable

esta documentación y varias otras fuentes me llevaron a creer que puedo hacer eso usando tabindex atributo (correspondiente a HTMLElement.tabIndex propiedad). Sin embargo, cuando trato de asignar tabindex a mi sello, sigue tan desenfocado como siempre, por mucho que intente Tab lo.

¿Por qué no tabindex hacer la etiqueta enfocable?

El fragmento siguiente demuestra el problema. Si enfoca la entrada con el mouse e intenta enfocar la etiqueta usando Tabno funciona (enfoca lo siguiente <span> con tabindex en cambio).

document.getElementById('checkbox').addEventListener('change', function (event) {
  document.getElementById('val').innerHTML = event.target.checked;
});
<div>
  <input type="text" value="input">
</div>
<div>
  <label tabindex="0">
    <input type="checkbox" id="checkbox" style="display:none;">
    checkbox: <span id="val">false</span>
  </label>
</div>
<span tabindex="0">span with tabindex</span>

(El código JavaScript solo permite ver que hacer clic en la etiqueta correctamente (des)marca la casilla de verificación).

  • una etiqueta no es una capa, ni una entrada, es solo un elemento esquemático

    – Freak no calificado

    18 de diciembre de 2014 a las 15:58

  • @UnskilledFreak ¿Por qué haría eso tabindex no trabajar en eso? he añadido un <span> a mi código para demostrar mejor el problema.

    – hon2a

    18 de diciembre de 2014 a las 16:03


  • tabindex es “prehistórico” para saltar de una entrada a otra sin el uso de un mouse, entonces ¿POR QUÉ querrías saltar a una etiqueta? ¿Cual es la razón?

    – Freak no calificado

    18 de diciembre de 2014 a las 16:47

  • tabindex es todavía muy importante hoy en día para la accesibilidad. Adicionalmente, todavía es parte de la especificación HTML. Por último, tabindex es a válido atributo por label elementos.

    – Sansón

    18 de diciembre de 2014 a las 19:32

avatar de usuario
abhitalks

¿Por qué tabindex no hace que la etiqueta sea enfocable?

Respuesta corta:

  1. La etiqueta es enfocable.
  2. TabIndex no hará ninguna diferencia.
  3. Bienvenido al mundo de las incoherencias entre navegadores y agentes.

tl;dr;

los label (Árbitro) elemento es muy enfocable. Su interfaz DOM es HTMLLabelElement que se deriva de HTMLElement (Árbitro) que a su vez implementa GlobalEventHandlers (Árbitro) y por lo tanto expone la focus() método y onfocus controlador de eventos.

La razón por la que no puede obtener la especificación adecuada/documento de referencia para labelEl comportamiento de enfoque de s se debe a que es posible que haya estado mirando las especificaciones de HTML5. Curiosamente, las referencias de HTML5 no indican nada relacionado con eso, lo que aumenta la confusión.

Esto se menciona en HTML 4.01 Ref aquí: http://www.w3.org/TR/html401/interact/forms.html#h-17.9.1

Específicamente cerca del final de la sección 17.9.1 y justo antes de la 17.10:

Cuando un elemento LABEL recibe el foco, pasa el foco a su control asociado.

Además, en otro lugar (No puedo conseguir esa parte de la referencia.) He leído que depende del agente ejecutor. (No confíes en mi palabra, no estoy muy seguro.).

Sin embargo, lo que significa es que cuando focus a label (o un label recibió un focus), que focus se pasa a su control etiquetable asociado. Esto no resultará en dos diferentes focuses, pero uno focus sobre el input (en su caso una casilla de verificación). Debido a este comportamiento, tabindex la propiedad no puede jugar un papel.

También hay un conjunto de pruebas de W3C para accesibilidad de sitios web (WAAG) aquí: http://www.w3.org/WAI/UA/TS/html401/cp0102/0102-ONFOCUS-ONBLUR-LABEL.html que, analiza la implementación de onfocus y onblur para label. Idealmente un teclado o una tecnología de asistencia que emula el teclado debería implementar esto. Pero…

Aquí es donde las inconsistencias del navegador juegan su papel.

Esto se puede demostrar con este ejemplo. Verifique el siguiente fragmento en diferentes navegadores. (Lo he probado contra IE-11, GC-39 y FF-34. Todos se comportan de manera diferente).

  1. Haga clic en el botón “Etiqueta de enfoque
  2. Debe enfocar la etiqueta, luego pasar el foco y resaltar el contorno de la casilla de verificación asociada en azul.
  3. Chrome-v39 funciona. IE-v11 no lo hace (de alguna manera, html y el cuerpo responden a: enfoque). FF-v34 funciona.

Hablando de inconsistencias del navegador, intente usar la “clave de acceso” L. Algunos navegadores seleccionarán la casilla de verificación mientras que otros harán clic en ella, es decir, le pasarán la acción.

Aquí hay un violín para probarlo: http://jsfiddle.net/abhitalks/ff0xds4z/2/

Aquí hay un retazo:

label = $("label").first();
$("#btn").on("click", function() {
    label.focus();
});
* { margin: 8px; }
.highlight { background-color: yellow; }
:focus {
    outline: 2px solid blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="txt" type="text" value="input" /><br />
<label for="chk" accesskey="L">Checkbox: </label>
<input id="chk" type="checkbox" /><br />
<input id="btn" type="button" value="Focus Label" />

Espero que aclare tus dudas.

.


Tu problema:

Ahora focuscantar (sic) en su problema original de no poder enfocar una etiqueta, porque desea diseñar una casilla de verificación de manera diferente al colocar un ícono en su lugar.

Para hacer eso, una opción para usted es no ocultarlo completamente haciendo un display:none;. Más bien, hágalo de 1×1 píxel y colóquelo debajo de su icono. De esta manera, seguirá recibiendo el foco de forma natural y, sin embargo, se ocultará de manera efectiva.

Por ejemplo, si sus iconos son un marca de verificación y un cruzluego cambie la posición de la casilla de verificación y haga que los íconos salgan de ::before o ::after pseudo-elementos en la etiqueta. Eso hará que la casilla de verificación siga recibiendo el foco y que el ícono responda a eso. Eso dará la aparente ilusión de que el ícono toma el foco.

Violín de demostración: http://jsfiddle.net/abhitalks/v0vxcw77/

Retazo:

div.chkGroup { position: relative; }
input#chk {
    position: absolute;
    width: 1px; height: 1px;
    margin: 0; margin-top: 4px; outline: none;
    border: 1px solid transparent; background-color: transparent;
}
label::before {
    content: '\2714';
    position: relative;
    width: 18px; height: 18px;
    background-color: #fff;
    margin-right: 8px; padding: 2px;
    display: inline-block; 
    border: 1px solid transparent;
}
input#chk:checked + label::before {
    content: '\2716';
}
input#chk:focus + label::before {
    border: 1px solid #00f;
}
<input id="txt" type="text" value="input" /><br /><br />
<div class="chkGroup">
    <input id="chk" type="checkbox" />
    <label for="chk" accesskey="L">Checkbox</label>
</div>

.

  • Tenía mis sospechas acerca de que la etiqueta transfiriera el foco a la entrada, pero no estaba seguro. Gracias por señalarme en la dirección correcta. Gracias también por la solución sugerida, pero voy con una táctica diferente. Su solución requiere poner un fondo opaco detrás del ícono, lo cual es bastante inconveniente. En cambio, estoy agregando automáticamente un “reemplazo” después de la casilla de verificación. Con tabindex y algunas secuencias de comandos, puedo transferir los eventos apropiados a la entrada (enviarlos en él). Como estoy usando AngularJS, es bastante fácil encapsular esto en un input directiva.

    – hon2a

    19 de diciembre de 2014 a las 18:04

  • @hon2a: Sí. Esa es una solución bastante buena también. Puede colocar un reemplazo en la casilla de verificación y manejarlo. Los mejores deseos.

    – Abhitalks

    19 de diciembre de 2014 a las 18:27

  • Solo me preguntaba, ¿cuál es un ejemplo de un navegador/plataforma que no marca la casilla de verificación en el primer fragmento al presionar Alt+Shift+l?

    – binki

    19 de diciembre de 2014 a las 19:14

  • Con respecto al primer fragmento nuevamente, parece que IE-11.0.8 está siguiendo Gestión de enfoque (a fecha de 2014-12-19). agregando tabindex hacia <label/> da como resultado que la etiqueta cumpla las condiciones y de repente el botón lo hace hacer que el enfoque cambie ;-). Tal vez el encadenamiento de enfoque esté ausente en los borradores de HTML5 porque el comportamiento depende de la implementación, pero parece que IE es respetando el borrador…

    – binki

    19 de diciembre de 2014 a las 20:03

Dado que esta publicación anterior es uno de los mejores resultados de Google para índice de tabulación de etiquetas html Quiero agregar mi solución de trabajo muy simple. Como @Abhitalks mencionó en la respuesta aceptada, el foco de una etiqueta se pasa a su control asociado. Entonces, para evitar este comportamiento, simplemente agregue un tabindex a la etiqueta y uso event.preventDefault() en un focus EventListener.

@Heretic Monkey tuvo la idea correcta en su respuesta, pero no necesita un elemento de envoltura para lograr esto. Sin embargo, deberá reenviar manualmente las pulsaciones de teclas requeridas (como la barra espaciadora).

Por ejemplo:

'use strict';

let field = document.getElementById('hidden-file-chooser');
let label = document.querySelector('label[for=hidden-file-chooser]');

// prevent focus passing
label.addEventListener('focus', event => {
  event.preventDefault();
});

// activate using spacebar
label.addEventListener('keyup', event => {
  if (event.keyCode == 32) {
    field.click();
  }
});
#hidden-file-chooser {
  display: none;
}

input[type=text] {
  display: block;
  width: 20rem;
  padding: 0.5rem;
}

label[for=hidden-file-chooser] {
  display: inline-block;
  background: deepskyblue;
  margin: 1rem;
  padding: 0.5rem 1rem;
  border: 0;
  border-radius: 0.2rem;
  box-shadow: 0 0 0.5rem 0 rgba(0,0,0,0.7);
  cursor: pointer;
}
<input type="text" placeholder="Click here and start tabbing through ...">

<input id="hidden-file-chooser" type="file">
<label for="hidden-file-chooser" tabindex="0"> Select a File </label>

<input type="text" placeholder="... then shift+tab to go back.">

pd: he usado input[type=file] en mi ejemplo porque eso es en lo que estaba trabajando cuando me encontré con este problema. Los mismos principios se aplican a cualquier tipo de entrada.

  • Solo quería dejar un comentario para agradecerle específicamente a @Besworks por su respuesta. ¡Justo lo que necesitaba! Lo único que me queda por descubrir es cómo evitar que se detenga en la casilla de verificación oculta mientras se desplaza por las entradas. ¡Pero al menos mi etiqueta (diseñada para parecerse a un botón de menú) muestra correctamente los estilos de enfoque ahora!

    – Tyler Youngblood

    6 oct 2020 a las 13:42

  • Actualización: Agregando tabstop="-1" a la casilla de verificación evita que se enfoque. Para que se pase por alto mientras se tabula a través de las entradas. También noté que la barra espaciadora desplazaba la página, así que agregué un e.preventDefault() al evento keydown (no keyup) que sigue la entrada de la barra espaciadora. También agregué un keyup para la tecla enter (keyCode == 13). ¡Todo funciona perfectamente ahora!

    – Tyler Youngblood

    6 oct 2020 a las 15:46

avatar de usuario
mono hereje

Editar: Lo siguiente fue una mala lectura de la especificación:

Mirando eso la especificación completaverás que hay algo llamado indicador de enfoque tabindexque define si el tabindex El atributo realmente hará que el campo sea “tabable”. los label falta un elemento en esa lista de elementos sugeridos.

Pero, de nuevo, también lo es el span elemento, así que imagínense :).

Dicho esto, sPuede hacer que el texto de la etiqueta sea enfocable envolviendo todo en otro elemento, o usando JavaScript para forzar el problema. Desafortunadamente, envolver (aquí en un ancla) puede generar una buena cantidad de trabajo adicional en CSS y JS para que funcione como un programa normal. label elemento.

document.getElementById('checkbox').addEventListener('change', function(event) {
  document.getElementById('val').innerHTML = event.target.checked;
});
document.getElementsByClassName('label')[0].addEventListener('click', function(event) {
  event.target.getElementsByTagName('label')[0].click();
  event.preventDefault();
});
document.getElementsByClassName('label')[0].addEventListener('keypress', function(event) {
  if ((event.key || event.which || event.keyCode) === 32) {
    event.target.getElementsByTagName('label')[0].click();
    event.preventDefault();
  }
});
.label,
.label:visited,
.label:hover,
.label:active {
  text-decoration: none;
  color: black;
}
<div>
  <input type="text" value="input">
</div>
<div>
  <a class="label" href="#">
    <label tabindex="0">
      <input type="checkbox" id="checkbox" style="display:none;">checkbox: <span id="val">false</span>
    </label>
  </a>
</div>
<span tabindex="0">span with tabindex</span>

  • La sección de especificaciones que observa enumera los elementos que los proveedores de navegadores deben establecer en el indicador de enfoque de forma predeterminada (es decir, no tabindex atributo requerido para que sean enfocables)

    – steveax

    19 de diciembre de 2014 a las 0:31

  • Perdón por no leer la documentación vinculada antes. Por lo que puedo ver, @steveax tiene razón. La documentación establece que algunos de los elementos deben tener la indicador de enfoque tabindex establecido por defecto (y label no está entre ellos), pero de lo contrario esa bandera debe izarse en presencia de tabindex atributo. Entonces, tengo que anular la aceptación de la respuesta.

    – hon2a

    19 de diciembre de 2014 a las 10:17

  • @MikeMcCaughan Es posible que desee editar su respuesta, ya que la información al principio es engañosa.

    – hon2a

    19 de diciembre de 2014 a las 18:06

Como decían los carteles anteriores:
El foco de la etiqueta siempre va directamente al elemento de entrada.

Una gran molestia si alguien tiene casillas de verificación elegantes (pero falsas), ocultando las originales, con un enfoque real para la navegación del teclado que no se ve por ninguna parte.

mejor solución que se me ocurre: javascript.

Elimine el enfoque real, a favor de uno falso:

    input[type=checkbox]:focus {
        outline: none;
    }
    .pseudo-focus {
        outline: 2px solid blue;
    }

y esté atento a los cambios en la casilla de verificación original (en muchos escenarios visiblemente oculta):

        $('entrada[type=checkbox')
        .focus( function() {
            $(this).closest('label').addClass('pseudo-focus');
        })
        .blur( function() {
            $(this).closest('label').removeClass('pseudo-focus');    
        });

Full jsfiddle here.

For input type radio or checkbox:

opacity: 0;
height: 0;
width: 0;
min-height: 0;
line-height: 0;
margin: 0;
padding: 0;
border: 0 none;

and the Js above does the trick sweetly.

¿Ha sido útil esta solución?