Como usar o tabindex

Como modificar a ordem do DOM com tabindex

Dave Gash
Dave Gash
Meggin Kearney
Meggin Kearney

A ordem de tabulação padrão fornecida pela posição do DOM de elementos nativos é conveniente, mas há momentos em que você precisa modificar a ordem de tabulação, e mover fisicamente os elementos no HTML nem sempre é uma solução ideal ou viável. Nesses casos, você pode usar o atributo HTML tabindex para definir explicitamente a posição de um elemento na guia.

Compatibilidade com navegadores

  • 1
  • 12
  • 1,5
  • ≤4

Origem

A tabindex pode ser aplicada a qualquer elemento, embora não seja necessariamente útil em todos eles, e aceita um intervalo de valores inteiros. Com tabindex, é possível especificar uma ordem explícita para elementos de página focalizáveis, inserir um elemento que não seria de foco na ordem de tabulação e remover elementos da ordem de tabulação. Exemplo:

tabindex="0": insere um elemento na ordem natural de tabulação. Para focar o elemento, pressione a tecla Tab. Para focar, o elemento chama o método focus().

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

Pressione a tecla Tab para me concentrar.

tabindex="-1": remove um elemento da ordem natural de tabulação, mas o elemento ainda pode ser focado chamando o método focus().

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

// TODO: DevSite - O exemplo de código foi removido porque usava manipuladores de eventos inline

tabindex="5": qualquer tabindex maior que 0 pula o elemento para a frente da ordem natural de tabulação. Se houver vários elementos com um tabindex maior que 0, a ordem de tabulação vai começar pelo valor mais baixo que é maior que zero e vai subindo. Usar um tabindex maior que 0 é considerado um antipadrão.

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

Isso é particularmente verdadeiro para elementos não de entrada, como cabeçalhos, imagens ou títulos de artigos. Adicionar tabindex a esses tipos de elementos é contraprodutivo. Se possível, organize seu código-fonte de modo que a sequência do DOM forneça uma ordem lógica de tabulação. Se você usar o tabindex, restrinja-o a controles interativos personalizados, como botões, guias, menus suspensos e campos de texto, ou seja, elementos para os quais o usuário pode fornecer entradas.

Não se preocupe se os usuários de leitores de tela não detectarem conteúdo importante porque ele não tem uma tabindex. Mesmo que o conteúdo seja muito importante, como uma imagem, se não for algo com que o usuário possa interagir, não há motivo para torná-lo focalizável. Os usuários de leitores de tela ainda podem entender o conteúdo da imagem, desde que você forneça o suporte adequado para o atributo alt, que abordaremos em breve.

Gerenciamento do foco no nível da página

Confira um cenário em que tabindex não é apenas útil, mas necessário. Talvez você esteja criando uma única página robusta com diferentes seções de conteúdo, nem todas visíveis ao mesmo tempo. Nesse tipo de página, clicar em um link de navegação pode mudar o conteúdo visível sem precisar atualizar a página.

Quando isso acontecer, você provavelmente identificará a área de conteúdo selecionada, atribuirá a ela um tabindex de -1 para que não apareça na ordem natural de tabulação e chamará o método focus. Essa técnica, chamada de gerenciamento de foco, mantém o contexto percebido pelo usuário em sincronia com o conteúdo visual do site.

Gerenciamento do foco em componentes

Gerenciar o foco quando você muda algo na página é importante, mas às vezes é necessário gerenciar o foco no nível de controle, por exemplo, se você estiver criando um componente personalizado.

Considere o elemento select nativo. Ela pode receber foco básico, mas, depois, é possível usar as teclas de seta para expor outras funcionalidades (as opções selecionáveis). Se você estiver criando um elemento select personalizado, exponha esses mesmos tipos de comportamentos para que os usuários que dependem principalmente do teclado ainda possam interagir com seu controle.

<!-- 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 quais comportamentos de teclado implementar pode ser difícil, mas há um documento útil que você pode consultar. O guia Práticas de criação acessíveis de aplicativos avançados da Internet (ARIA, na sigla em inglês) lista os tipos de componentes e os tipos de ações de teclado compatíveis. Falaremos sobre ARIA com mais detalhes mais tarde, mas, por enquanto, vamos usar o guia para nos ajudar a adicionar suporte ao teclado a um novo componente.

Talvez você esteja trabalhando em alguns novos elementos personalizados que se parecem com um conjunto de botões de opção, mas com uma perspectiva única de aparência e comportamento.

<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 que tipo de suporte a teclado é necessário, consulte o guia de práticas de criação ARIA (em inglês). A seção 2 contém uma lista de padrões de design e, nessa lista, há uma tabela de características para grupos de rádio, o componente atual que melhor corresponde ao novo elemento.

Como mostrado na tabela, um dos comportamentos mais comuns do teclado que precisa ter suporte são as teclas de seta para cima/baixo/esquerda/direita. Para adicionar esse comportamento ao novo componente, usaremos uma técnica chamada tabindex itinerante.

Trecho de especificação de W3C para botões de opção.

O tabindex itinerante funciona definindo tabindex como -1 para todos os filhos, exceto o que está ativo no momento.

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

Em seguida, o componente usa um listener de eventos de teclado para determinar qual tecla o usuário pressiona. Quando isso acontece, ele define o tabindex do filho anteriormente em foco como -1, define o tabindex do filho a ser focado como 0 e chama o método de foco nele.

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

Quando o usuário chegar ao último (ou primeiro, dependendo da direção em que ele está movendo o foco), você fará uma volta e focará o primeiro (ou o último) filho novamente.

Você pode testar o exemplo completo abaixo. Inspecione o elemento no DevTools para observar o tabindex se movendo de um rádio para o próximo.

Água Café Chá Cola Ginger Ale

// TODO: DevSite - O exemplo de código foi removido porque usava manipuladores de eventos inline

Veja a fonte completa desse elemento (link em inglês) no GitHub.

traps modais e de teclado

Às vezes, quando está gerenciando o foco, você pode entrar em uma situação da qual não pode sair. Imagine um widget de preenchimento automático que tenta gerenciar o foco e captura o comportamento da guia, mas que impede o usuário de deixá-lo até que seja concluído. Isso é chamado de armadilha do teclado e pode ser muito frustrante para o usuário. A seção 2.1.2 da lista de verificação do Web AIM aborda esse problema, afirmando que o foco do teclado nunca deve ser bloqueado ou preso em um elemento específico da página. O usuário precisa conseguir navegar de e para todos os elementos da página usando apenas o teclado.

Estranhamente, há momentos em que esse comportamento é realmente desejável, como em uma janela modal. Normalmente, quando o modal é exibido, você não quer que o usuário acesse o conteúdo por trás dele. Você pode adicionar uma sobreposição para cobrir a página visualmente, mas isso não impede que o foco do teclado saia acidentalmente do modal.

Uma janela modal solicitando que o usuário salve o trabalho.

Em casos como esse, é possível implementar uma armadilha temporária do teclado para garantir que você retenha o foco somente enquanto o modal estiver sendo mostrado e, em seguida, restaurar o foco para o item focado anteriormente quando ele estiver fechado.

Existem algumas propostas sobre como facilitar isso para os desenvolvedores, incluindo o elemento <dialog>, mas elas ainda não têm suporte generalizado do navegador.

Consulte este artigo MDN para mais informações sobre <dialog> e este exemplo modal para mais informações sobre janelas modais.

Considere uma caixa de diálogo modal representada por uma div que contém alguns elementos e outra div que representa uma sobreposição de plano de fundo. Vamos conferir as etapas básicas necessárias para implementar uma armadilha temporária do teclado nessa situação.

  1. Usando document.querySelector, selecione os divs modal e de sobreposição e armazene as referências deles.
  2. À medida que o modal é aberto, armazene uma referência ao elemento que estava em foco quando o modal foi aberto para que você possa retornar o foco para esse elemento.
  3. Use um listener de keydown para capturar as chaves à medida que elas são pressionadas enquanto o modal está aberto. Você também pode detectar um clique na sobreposição de plano de fundo e fechar a janela modal se o usuário clicar nela.
  4. Em seguida, acesse a coleção de elementos focalizáveis dentro do modal. O primeiro e o último elementos focalizáveis vão atuar como "sentinelas" para que você saiba quando voltar o foco para frente ou para trás a fim de permanecer dentro do modal.
  5. Mostre a janela modal e mova o foco para o primeiro elemento focalizável.
  6. À medida que o usuário pressiona Tab ou Shift+Tab, mova o foco para a frente ou para trás, fazendo um loop no último ou primeiro elemento, conforme apropriado.
  7. Se o usuário pressionar Esc, feche a janela modal. Isso é muito útil, porque permite que o usuário feche o modal sem procurar um botão "Fechar" específico, além de beneficiar até mesmo os usuários que estejam usando um mouse.
  8. Quando o modal estiver fechado, oculte-o e à sobreposição de plano de fundo, e restaure o foco para o elemento focado anteriormente que foi salvo antes.

Este procedimento fornece uma janela modal utilizável e não frustrante que todos podem usar efetivamente.

Para mais detalhes, consulte este exemplo de código e veja um exemplo ativo em uma página concluída.