Crie Guias De interface Do Usuário Acessíveis Em JavaScript

As guias de interface são um componente comum do site, mas os desenvolvedores geralmente enfrentam desafios para tornar esse padrão acessível. Os usuários de leitores de tela podem encontrar alguns problemas ao ler o conteúdo, e os usuários que dependem de um teclado para navegar em um site podem precisar de assistência para facilitar a navegação.

Este artigo cobrirá tudo o que você precisa saber para criar uma interface de guia acessível, incluindo a construção da estrutura HTML, a adição do estilo CSS e a adição de aprimoramentos da funcionalidade JavaScript.

Aqui está a interface com guias que construiremos neste tutorial:

Interface com abas finalizada

Uma interface com guias listando diferentes países. À esquerda, as abas são exibidas verticalmente e à direita, há um texto fictício e uma imagem para o país selecionado (neste caso, Mongólia).

Vamos começar!

Estabelecendo a estrutura HTML

A estrutura HTML é muito importante porque fornece aos usuários de leitores de tela o contexto necessário para navegar na interface.

Existem duas maneiras de usar a estrutura HTML para ajudar a definir quais elementos podem ser focados com um teclado. Podemos usar uma abordagem baseada em link com o <a>elemento como nosso gatilho de guia ou podemos usar o <button>elemento.

Ambas as abordagens levam em consideração a navegação pelo teclado por serem nativamente focáveis ​​pelo teclado, mas cada uma tem vantagens e desvantagens. Abordaremos ambas as abordagens neste artigo.

Usando o <a>elemento como a guia

A principal vantagem dessa abordagem é que podemos usar a <a>funcionalidade do elemento para criar o efeito de navegação da guia usando apenas HTML e CSS. O <a>elemento traz suporte ao teclado cuidando muito do gerenciamento de foco e navegação para usuários de leitores de tela para nós!

Essa abordagem também é útil para usuários que têm o JavaScript desabilitado. Usaremos JavaScript apenas no final deste tutorial — para aprimorar algumas das <a>funcionalidades do elemento para fins de acessibilidade.

No entanto, uma pequena desvantagem de usar o <a>elemento como guia é que ele é contado no histórico de navegação do usuário. Em outras palavras, se o usuário visitar todas as abas e depois quiser retornar ao site anterior, terá que pressionar o botão voltar várias vezes.

Ainda assim, essa abordagem tem aprimoramento progressivo em mente, pois oferece a maior funcionalidade de interface de guia com a menor tecnologia possível. Essa abordagem funcionará mesmo que o JavaScript não carregue e mesmo que o CSS também falhe, então vale a pena considerar.

Usando o <button>elemento como a guia

Usar o <button>elemento como guia tem a vantagem de não afetar o histórico de navegação, facilitando o retorno do usuário ao site anterior.

No entanto, essa abordagem requer algumas considerações extras com HTML para ocultar todo o conteúdo de guias não ativas. Também requer algumas decisões adicionais em relação ao JavaScript, como determinar como exibir conteúdo, estratégias de gerenciamento de foco e alguns aprimoramentos de acessibilidade.

Se você usar essa abordagem, precisará considerar o que acontecerá se o JavaScript não carregar em seu site. Sem um fallback, os usuários não poderão navegar em sua interface com guias. Existem várias razões pelas quais o JavaScript pode falhar ao carregar. Por exemplo, os usuários podem ter o JavaScript desabilitado devido a questões de rastreamento e privacidade, ou o JavaScript pode falhar ao carregar devido a fatores externos.

Criando a estrutura da guia da interface

Depois de decidir qual abordagem você usará, é hora de começar a criar a estrutura HTML!

Estrutura da interface da guia com a <a>abordagem do elemento

Vamos começar com as guias individuais. Aqui, usaremos a <a>abordagem de elemento para a estrutura HTML:

<!-- Tab semantics -->
<ul role="tablist">
  <li role="presentation">
    <a role="tab" href="#panel1" id="tab1">Tab one</a>
  </li>
  <li role="presentation">
    <a role="tab" href="#panel2" id="tab2">Tab two</a>
  </li>
  <li role="presentation">
    <a role="tab" href="#panel3" id="tab3">Tab three</a>
  </li>
  <li role="presentation">
    <a role="tab" href="#panel4" id="tab4">Tab four</a>
  </li>
</ul>

<!-- Tab content semantics -->
<div class="tabpanel-container">
  <section role="tabpanel" id="panel1" aria-labelledby="tab1" tabindex="0"></section>
  <section role="tabpanel" id="panel2" aria-labelledby="tab2" tabindex="0"></section>
  <section role="tabpanel" id="panel3" aria-labelledby="tab3" tabindex="0"></section>
  <section role="tabpanel" id="panel4" aria-labelledby="tab4" tabindex="0"></section>
</div>

Agora, vamos examinar a estrutura mostrada no código acima:

  • No final do dia, uma lista de guias é apenas uma lista de links (ou botões) que nos levarão ao nosso conteúdo, então a semântica correta para a lista de guias é envolvê-la com nosso elemento selecionado ( <a>ou <button>) dentro da lista itens dentro de um <ul>elemento
  • Adicionamos a tablistfunção ao nosso <ul>para fornecer mais contexto para usuários de leitores de tela; essa função é usada para marcar um contêiner que envolve um conjunto de guias. Como o tablistpapel não fornece a semântica de tabulação ao elemento filho, adicionamos o tabpapel ao nosso <a>elemento
  • Se um usuário de leitor de tela não conseguir ler as funções tabliste tab, o HTML ainda será lido como uma lista de links ou botões, o que é um substituto aceitável
  • Adicionamos o presentationpapel aos <li>elementos apenas para remover a semântica; isso ajudará a evitar interações estranhas, mantendo nosso fallback caso um leitor de tela específico não ofereça suporte a funções ARIA
  • Nosso conteúdo da guia será representado por um <section>elemento com o tabpanelpapel. Espera-se que esse elemento use o mesmo nome da guia como um nome acessível. É por isso que adicionamos o idatributo às nossas guias e o estamos usando como um rótulo em nossos painéis de guias com o aria-labelledbyatributo
  • Adicionamos o atributo tabindex="0"aos nossos painéis de guias para permitir que itens dentro da guia (como campos de formulário, links ou botões) recebam o foco do teclado. Isso tornará mais fácil para os usuários de teclado acessar o conteúdo

Estrutura da interface da guia com a <button>abordagem do elemento

Se usarmos a <button>abordagem baseada em -, precisaremos incluir uma etapa extra. Precisaremos adicionar o hiddenatributo a todos os painéis de guias, exceto a primeira guia. Este atributo ocultará os elementos para usuários com visão e usuários de leitores de tela. Para garantir que a primeira guia permaneça visível, ela deve ter o aria-selected="true"atributo.

Nossa marcação para a <button>abordagem deve ser algo assim:

<!-- Tab semantics -->
<ul role="tablist">
  <li role="presentation">
    <button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel2" id="tab2">Tab two</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel3" id="tab3">Tab three</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel4" id="tab4">Tab four</button>
  </li>
</ul>

<!-- Tab content semantics -->
<div class="tabpanel-container">
  <section role="tabpanel" id="panel1" aria-labelledby="tab1" tabindex="0"></section>
  <section role="tabpanel" id="panel2" aria-labelledby="tab2" tabindex="0" hidden></section>
  <section role="tabpanel" id="panel3" aria-labelledby="tab3" tabindex="0" hidden></section>
  <section role="tabpanel" id="panel4" aria-labelledby="tab4" tabindex="0" hidden></section>
</div>

Considerações adicionais sobre a estrutura HTML

Até agora, cobrimos as considerações iniciais para nossa marcação, mas há fatores adicionais que precisamos ter em mente para determinados casos de uso. Alguns fatores serão controlados de forma mais dinâmica, mas falaremos sobre eles um pouco mais adiante na seção JavaScript deste artigo.

Relacionando abas com o painel de abas

Podemos relacionar as guias com o painel de guias para usuários de leitores de tela usando o atributo aria-controls. No entanto, essa abordagem funcionará apenas no leitor de tela JAWS e seu uso pode parecer bastante detalhado, conforme explicado no artigo de Heydon Pickering . Em vez disso, usaremos algumas estratégias de gerenciamento de foco que também ajudarão os usuários de teclado.

Manipulando guias verticais

Para exibir uma lista de guias com orientação vertical, você precisará adicionar o atributo [aria-orientation="vertical"]ao contêiner. Isso indicará aos usuários de leitores de tela que as guias estão empilhadas verticalmente.

Isso será importante quando começarmos a discutir estratégias de navegação para nosso exemplo de interface com guias.

Aqui está um exemplo mostrando como o [aria-orientation="vertical"]atributo seria usado em um snippet de código:

<ul role="tablist" aria-orientation="vertical">
  <li role="presentation">
    <button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel2" id="tab2">Tab two</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel3" id="tab3">Tab three</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel4" id="tab4">Tab four</button>
  </li>
</ul>

Indicando a seleção da guia

Quando um usuário clicar em uma guia, precisaremos de uma maneira de indicar que ela foi selecionada.

Usaremos o atributo [aria-selected="true"]para indicar a seleção de guias para usuários de leitores de tela. Em seguida, vamos estilizá-lo com CSS para ajudar a indicar a seleção para usuários com visão. O [aria-selected="true"]atributo mudará dinamicamente com JavaScript, portanto, não será adicionado à nossa marcação inicial.

<li role="presentation">
  <button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
</li>

Agora que temos uma estrutura HTML sólida, é hora de adicionar algum estilo com CSS !

Adicionando estilo e layout com CSS

Para nosso exemplo de interface com guias, estamos usando uma abordagem baseada em link com o <a>elemento como nosso gatilho de guia. Para estilizar a estrutura de guias HTML, precisaremos adicionar a navegação de guias, criar o indicador de foco e identificar o estilo do estado selecionado.

Adicionando a navegação da guia

Para adicionar a navegação da guia, vamos adicionar uma regra CSS usando o :targetseletor assim:

.tabpanel:not(:target):not(.visible) {
  display: none;
}

:targeté uma pseudo-classe que verifica se um elemento com um idcorresponde ao fragmento da URL.

Como estamos usando o <a>elemento aqui para adicionar funcionalidade, podemos usar esse seletor para ocultar quaisquer painéis de guias não ativos. Estamos usando a visibleclasse como uma exceção em nosso exemplo para que possamos adicionar alguns aprimoramentos com JavaScript posteriormente .

Criando o indicador de foco

Outra coisa que precisamos ter em mente é a navegação pelo teclado, e uma das coisas mais importantes a fazer nesse sentido é criar um indicador de foco adequado.

Você provavelmente notou um indicador de foco ao navegar em um site usando a tecla tab . Dependendo do seu navegador, aparecerá como um contorno azul ou preto destacando um elemento selecionado.

Os navegadores oferecem estilos de foco padrão, mas não são suficientes para atender aos critérios WCAG 2.2 . Portanto, é necessário usar um estilo adicional. Para obter informações mais detalhadas sobre como criar um bom indicador de foco, leia o artigo de Sara Souiedan sobre este tópico.

Para nosso exemplo, usaremos os seguintes estilos de foco:

.tab:focus-visible {
  background-color: royalblue;
  color: whitesmoke;
  outline: 0.2em solid transparent;
}

Decidi usar :focus-visibleem vez da :focuspseudo-classe para que o estilo de foco seja ativado apenas com a navegação do teclado. A :focuspseudo-classe também será ativada quando o elemento for clicado, o que pode ser confuso para os usuários.

:focus-visibleé muito bem suportado em navegadores modernos, por isso não deve criar nenhum tipo de conflito. Para obter mais informações sobre essa propriedade, consulte a documentação do MDN .

A opção “Mongólia” selecionada abaixo mostra como ficaria o estado de foco da nossa guia:

Guia Mongólia

A primeira guia na lista de guias do projeto tem um fundo azul, fornecendo uma dica visual para a seleção de guias para usuários de teclado.

Para este projeto, decidi usar um contorno transparente. Isso é importante para o modo de alto contraste do Windows . Nesse modo, todas as cores do texto e do plano de fundo são substituídas pelas escolhidas pelo sistema operacional, portanto, não podemos depender da cor do plano de fundo para indicar um estado de foco. O contorno é a única maneira confiável de indicar o estado do foco.

Identificando o estilo do estado selecionado

Agora, vamos voltar ao que mencionei anteriormente sobre o uso do aria-selected="true"atributo para ajudar os usuários de leitores de tela a identificar mais facilmente a guia selecionada. Podemos usar esse seletor para fornecer uma dica visual para a guia selecionada também!

.tab[aria-selected="true"] {
  background-color: var(--component-bg);
}

Essa abordagem cria um pequeno problema devido ao funcionamento da especificidade do CSS. Ambos os seletores .tab:focus-visiblee .tab[aria-selected="true"], têm o mesmo nível de especificidade e ambos alteram a background-colorpropriedade, portanto, a ordem da regra do seletor é crítica.

Queremos que o estado de foco background-colorsobrescreva qualquer outro background-color, incluindo o estado selecionado, então adicionaremos a .tab:focus-visibleregra seletora após a .tab[aria-selected="true"]regra seletora.

Aqui está o nosso resultado; observe como os dois estilos interagem entre si sem nenhum problema de especificidade:

Guia de Mianmar

A primeira guia na lista de guias do projeto está ativada e tem um fundo cinza, enquanto a terceira guia está focada no teclado e tem um fundo azul.

Se você estiver usando o <a>elemento como sua guia, você já criou uma interface de guia funcional com a menor tecnologia possível!

Se você usou a <button>abordagem baseada em -, não se preocupe! Em seguida, adicionaremos algumas funcionalidades com JavaScript, que serão cruciais para tornar a interface com abas mais acessível.

Adicionando melhorias e funcionalidades com JavaScript

Há muito para descompactar nesta seção; vamos começar verificando o que o ARIA Authoring Practices Guide (APG) tem a dizer sobre esse padrão de componente. Aqui está o que o guia de padrões do ARIA APG para a interface de guia considera importante para a tecla tabtabindex , os elementos in tabpanele as teclas de seta .

Gerenciando a tecla tab

Em relação ao uso da tecla tab , o ARIA APG sugere o seguinte :

Quando o foco se move para a lista de guias, coloque o foco no tabelemento ativo.

Nossa primeira tarefa é remover a navegação do teclado com a tecla tab para a guia não selecionada para que, quando a tecla for pressionada, o foco vá diretamente para o painel de guias ativo. Então, quando shift + tab é pressionado no painel ativo, o foco volta para sua respectiva aba. Além disso, teremos que indicar a seleção de guias para usuários de leitores de tela e usuários com visão.

Aqui está o código que usei para resolver essas tarefas:

const TABLIST = document.querySelector("#tablist");
const TABS = [...TABLIST.querySelectorAll(".tab")];

const setSelectedTab = (element) => {
  const selectedId = element.id;

  TABS.forEach((e) => {
    const id = e.getAttribute("id");
    if (id === selectedId) {
      e.removeAttribute("tabindex");
      e.setAttribute("aria-selected", "true");
    } else {
      e.setAttribute("tabindex", "-1");
      e.setAttribute("aria-selected", "false");
    }
  });
};

Aqui, selecionamos todas as abas e armazenamos aquela que está sendo clicada. Em seguida, analisamos o array com todas as nossas guias e comparamos cada idatributo para verificar se é a guia que foi selecionada.

Se a guia não for a que desejamos selecionar, adicionamos os atributos tabindex="-1"e . aria-selected="false"No entanto, se a guia for a que desejamos selecionar, removemos o tabindexatributo e adicionamos o aria-selected="true"atributo. Os elementos <a>e <button>são focados no teclado por padrão, portanto, não há necessidade de adicionar um arquivo tabindex="0".

O tabindex="-1"atributo tornará um elemento não focalizável com a tecla tab . Quando a tecla é pressionada, ela move o foco diretamente para o nosso painel de guias ativo, permitindo pular todas as nossas guias. Isso também é importante porque também nos permitirá gerenciar o foco das guias em outra que demonstrarei um pouco mais adiante neste artigo .

Agora tudo o que precisamos fazer é adicionar ouvintes de eventos às nossas guias para executar esta função! Para uma abordagem baseada em link, precisaremos adicionar um ouvinte de evento adicional a esses elementos.

Com uma <button>abordagem baseada, qualquer evento de clique será ativado pelas teclas enter e espaço . No entanto, um <a>elemento só adicionará esses eventos com a tecla enter , portanto, precisaremos adicionar um ouvinte de evento keydown para verificar quando a tecla de espaço é pressionada.

const handleClick = () => {
  TABS.forEach((element) => {
    element.addEventListener("click", function () {
      setSelectedTab(element);
    });
  });

  // Activates tab with Space. Only necessary if you use the <a> element as a tab

  TABS.forEach((element) => {
    element.addEventListener("keydown", function (e) {
      if ((e.keyCode || e.which) === 32) {
        setSelectedTab(element);
        element.click();
      }
    });
  });
};

Aqui está o nosso resultado!

Navegação de guias verticais

Projeto de amostra com navegação de guia vertical. Quando a tecla tab é pressionada, ela move o foco do teclado para o painel de guias; quando shift+tab é pressionado, ele move o foco para a guia selecionada.

Gerenciando os elementos tabindexintabpanel

O ARIA APG recomenda o seguinte para a interação do teclado da lista de guias :

Quando a lista de guias contém o foco, [tab] move o foco para o próximo elemento na sequência de guias da página fora da lista de guias, que é o painel de guias, a menos que o primeiro elemento contendo conteúdo significativo dentro do painel de guias seja focalizável.

Com base nessa recomendação, nossa próxima tarefa é verificar se cada painel de guia contém um elemento focalizável.

Para painéis de guias que não contêm um elemento focalizável, manteremos o tabindex="0"atributo que adicionamos anteriormente. Caso contrário, atualizaremos o atributo para tabindex="-1", portanto, quando a tecla tab for pressionada, o foco será movido para o primeiro elemento focalizável dentro do painel de guias.

Usaremos este snippet de código para verificar se cada painel de guia contém elementos focalizáveis ​​e alterar o tabindexatributo conforme necessário:

const TABPANELS = [...document.querySelectorAll(".tabpanel")];

const determineTabindex = () => {
  TABPANELS.forEach((element) => {
    const focusableElements = element.querySelectorAll(
      'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled]), details:not([disabled]), summary:not(:disabled)'
    ).length;

    focusableElements
      ? element.setAttribute("tabindex", "-1")
      : element.setAttribute("tabindex", "0");
  });
};

Aqui está o nosso resultado!

Navegação do teclado

Interface com guias com navegação por teclado. Quando um painel de guias tem um elemento focalizável e navegamos usando a tecla tab, o foco é movido para o primeiro elemento focalizável. Caso contrário, o foco estará no próprio painel de guias.

Neste exemplo, quando tentamos usar nosso teclado para navegar na interface, o elemento focado varia dependendo se o painel de guias contém um elemento focado no teclado.

Gerenciando a navegação com as teclas de seta

O ARIA APG oferece recomendações específicas de navegação pelo teclado, dependendo da orientação das guias da interface.

Aqui estão as recomendações quando o foco está em um elemento de guia em uma lista de guias horizontais :

seta para a esquerda : Move o foco para a guia anterior. Se o foco estiver na primeira guia, mova o foco para a última guia. Opcionalmente, ativa a guia recém-focada.
seta para a direita : Move o foco para a próxima guia. Se o foco estiver no último elemento da guia, mova o foco para a primeira guia. Opcionalmente, ativa a guia recém-focada.

Se você estiver construindo uma interface com abas exibidas verticalmente, além de usar o atributo aria-orientation="vertical", você terá que usar as teclas de seta para cima e seta para baixo em vez das teclas de seta para a esquerda e seta para a direita .

Aqui estão as recomendações quando uma lista de guias tem orientação de ária definida como vertical :

Quando uma lista de guias tem sua orientação de ária definida como vertical:
a seta para baixo funciona como a seta para a direita descrita acima.
seta para cima funciona como a seta para a esquerda é descrita acima.

Minha intenção para este projeto era construir uma interface completamente flexível, mas com essa abordagem em algum momento a tela acabará sendo uma lista de abas horizontais ou uma lista de abas verticais. Como abordamos isso?

Vamos começar determinando quais teclas de seta devemos usar. Neste caso específico, temos duas alternativas:

  1. Verifique a largura e a altura da lista de guias. Se a altura for maior que a largura, adicione o atributo aria-orientation="vertical"e dê suporte apenas à navegação de seta para cima e seta para baixo. Caso contrário, adicione o atributo e dê suporte apenas à navegação de seta para a esquerda e seta para a direita .aria-orientation="horizontal"
  2. Apoie todas as quatro teclas de navegação ao mesmo tempo. Se o usuário pressionar a seta para baixo ou a seta para a direita , ele moverá o foco para a próxima guia. Se eles pressionarem a seta para a esquerda ou a seta para cima , ele moverá o foco para a guia anterior.

Eu geralmente prefiro a solução mais simples, então escolho a segunda opção. No entanto, cada projeto é diferente. Se sua lista de guias estiver sempre na posição vertical, é melhor usar aria-orientation="vertical"e oferecer suporte apenas à navegação com seta para cima e seta para baixo .

Aqui está o código que usei, com base na segunda opção:

const createArrowNavigation = () => {
  const firstTab = TABS[0];
  const lastTab = TABS[TABS.length - 1];

  TABS.forEach((element) => {
    element.addEventListener("keydown", function (e) {
      if ((e.keyCode || e.which) === 38 || (e.keyCode || e.which) === 37) {
        if (element == firstTab) {
          e.preventDefault();
          lastTab.focus();
        } else {
          e.preventDefault();
          const focusableElement = TABS.indexOf(element) - 1;
          TABS[focusableElement].focus();
        }
      } else if (
        (e.keyCode || e.which) === 40 ||
        (e.keyCode || e.which) === 39
      ) {
        if (element == lastTab) {
          e.preventDefault();
          firstTab.focus();
        } else {
          e.preventDefault();
          const focusableElement = TABS.indexOf(element) + 1;
          TABS[focusableElement].focus();
        }
      }
    });
  });
};

Anteriormente, criamos uma variável que armazena todas as nossas guias. Podemos usá-lo para determinar qual é a primeira e a última guia de navegação. Isso é importante porque essas guias têm um comportamento especial.

Em seguida, podemos verificar quais códigos de tecla estão relacionados a cada tecla de seta. Para isso, podemos usar o site keycode.info para conferir. Para facilitar o processo, aqui está a lista dos códigos-chave:

ChaveCódigo chave
Seta esquerda37
Seta para cima38
Seta direita39
Seta para baixo40

Agora, ouvimos cada tecla para verificar a posição da guia focada no array e focamos no próximo elemento (a seta para a direita e a seta para baixo ) ou o elemento anterior (a seta para a esquerda e a seta para cima ) no array com o focus()método . Como esses elementos têm o tabindex="-1"atributo, poderemos focalizá-los com esse método.

Tenha em mente que se o elemento for o primeiro no array, ele moverá o foco para o último elemento e vice-versa. Outro ponto a ser considerado é que as teclas de seta também têm a função de mover a barra de rolagem; podemos evitar esse comportamento com o e.preventDefault()método.

Aqui está nossa interface, mostrando o uso das teclas de seta para mover entre as abas:

Usando as teclas de seta

Interface com guias com navegação pelo teclado, usando as teclas de seta para mover entre as guias.

Determinando como mostrar o conteúdo

Além de gerenciar o estado de foco de nosso componente, precisamos determinar o tempo para ativar a guia recém-focada. Em outras palavras, quando o painel de guias deve ser exibido? Deve ser mostrado quando a guia é clicada ou quando o foco é colocado na guia selecionada?

A resposta a este tópico é surpreendentemente matizada, e o W3C tem uma seção inteira sobre este tópico . Para resumir o W3C, alterar o conteúdo exibido no momento de focar um elemento (chamado de follow focus) pode ser benéfico para certos componentes, mas cria erros de acessibilidade para outros.

Exibindo conteúdo no momento do clique

A exibição de conteúdo no momento do foco pode facilitar a navegação pelo teclado para usuários com visão, mas os usuários de leitores de tela podem não estar cientes de que um novo conteúdo foi adicionado à página. Além disso, precisamos considerar a quantidade de conteúdo que será exibida, pois isso pode afetar o desempenho.

Sugiro exibir o conteúdo quando a guia for clicada. Essa opção pode exigir um pressionamento extra de teclas para usuários de teclado, mas fornecerá uma experiência mais inclusiva para usuários de leitores de tela.

Quando o <a>elemento é clicado, o foco é trazido para o painel de guias selecionado, que abrangerá a navegação para usuários de leitores de tela.

Exibindo conteúdo no momento do foco

Se você decidir exibir o conteúdo com base no foco, certifique-se de usar a <button>abordagem, em vez da <a>abordagem do elemento.

Com um <button>elemento, você precisará lidar com o gerenciamento de foco com JavaScript. Para isso, voltaremos à handleClickfunção que criamos anteriormente na seção CSS deste artigo e adicionaremos alguns ajustes.

Primeiro, criaremos uma função que adiciona ou remove o atributo hiddendependendo se o painel de guias está sendo direcionado. Se o painel de guias for o item de destino, também moveremos o foco do teclado para ele. Aqui está a minha abordagem para esta questão:

const showActivePanel = (element) => {
  const selectedId = element.id;
  TABPANELS.forEach((e) => {
    e.hidden = "true";
  });
  const activePanel = document.querySelector(
    `[aria-labelledby="${selectedId}"]`
  );
  activePanel.removeAttribute("hidden");
        activePanel.focus()
};

O que estamos fazendo aqui é ocultar todos os painéis de guias adicionando a eles o hiddenatributo. Em seguida, selecionaremos nosso painel de guias de destino usando o aria-labelledbyvalor do atributo. Lembre-se, cada buttonum tem um idque estamos usando para rotular o painel de guias usando este atributo; isso nos ajudará a selecionar o painel de guias correto.

Agora, apenas removemos o hiddenatributo e, em seguida, trazemos o foco do teclado para ele com o focus()método. Por fim, adicionamos a showActivePanelfunção em nossa handleClickfunção para ser executada no clique.

Agora é hora de ver como o componente funciona como um todo!

Clique em Navegação

Interface com abas mostrando o conteúdo no clique; as teclas de seta são usadas para navegação do teclado entre as guias.

Manipulando o <a>estado inicial do elemento

Se optarmos por usar um <a>elemento para nossas guias, precisaremos lidar com o estado inicial do elemento se o JavaScript estiver ativo.

Aqui estão as ações que queremos realizar quando o documento for carregado:

  • Se a URL do site não estiver apontando para os painéis de abas, o primeiro painel deve estar visível, a primeira aba deve ter o atributo aria-selected="true"e ser focável pelo teclado quando a tecla tab for pressionada; as outras abas devem ter aria-selected="false"e tabindex="-1"atributos
  • Se o URL do site estiver apontando para um painel, o painel nesse URL deverá estar visível; a respectiva aba deve ter os atributos mencionados anteriormente: aria-selected="true"eno tabindex

Vamos criar as funções para ambos os cenários.

const activateFirstPanel = () => {
  TABS[0].setAttribute("tabindex", "0");
  TABS[0].setAttribute("aria-selected", "true");
  TABPANELS[0].classList.add("visible");
};

Esta função cobrirá nosso primeiro caso. Na verdade é bem simples. Já temos as listas de nós TABSe TABPANELSnós, podemos selecionar o primeiro elemento de cada lista e adicionar as propriedades necessárias.

Na seção CSS anterior, mencionei que estávamos usando a visibleclasse como uma exceção e adicionaríamos alguns aprimoramentos com JavaScript posteriormente. Bem, agora é o momento para os aprimoramentos do JavaScript!

Adicionamos essa classe ao primeiro painel de guias para torná-la visível. Assim que o usuário começar a interagir com a guia, precisaremos remover essa classe.

const checkInitialSelectedTab = () => {
  const targetedTabPanel = document
    .querySelector(".tabpanel:target")
    .getAttribute("aria-labelledby");
  const selectedTab = document.querySelector(`#${targetedTabPanel}`);
  selectedTab.setAttribute("aria-selected", "true");
  selectedTab.removeAttribute("tabindex");
};

Nosso segundo cenário é um pouco mais complicado. Como este painel está sendo direcionado, ele será exibido na tela por padrão - isso se deve ao seletor
.tabpanel:not(:target):not(.visible)que usamos anteriormente. Mas, precisamos procurar a aba que ativa o painel.

Para isso, selecionaremos o painel de guias direcionado com JavaScript usando o .tabpanel:targetseletor no querySelector()método. Assim que tivermos esse nó, obteremos o aria-labelledbyatributo. A guia idé a mesma que estamos usando no aria-labelledbyatributo deste painel, então usaremos isso idpara procurar a guia e adicionaremos os atributos necessários.

Agora, só precisamos executar essas funções dependendo do que a URL contém, então usaremos outra função para lidar com isso:

const handleInitialState = () => {
  TABS.forEach((e) => {
    e.setAttribute("tabindex", "-1");
    e.setAttribute("aria-selected", "false");
  });

  window.location.href.indexOf("#panel") === -1
    ? activateFirstPanel()
    : checkInitialSelectedTab();

  determineTabindex();
};

Bem, eu menti. Há apenas um pouco mais de trabalho que precisamos fazer nesta função.

Para começar, vamos definir o aria-selectedatributo de todas as guias para falsee o tabindexatributo para -1; então vamos corrigir o correto com as funções criadas.

Em seguida, precisamos verificar se um painel de guias está sendo segmentado na URL do site. Podemos usar o window.location.hrefmétodo para obter a URL e, em seguida, usar o indexOfmétodo array para verificar se existe um painel direcionado a essa URL.

Quando você usa uma função array em uma string, como nossa URL, ela vai criar uma array com cada caractere da string como um elemento dessa array, se o mundo inteiro estiver nessa URL, ela vai retornar a posição inicial de a string, se não, retornará -1. Isso é o que usaremos para determinar qual função executar.

Agora, vamos executar a determineTabindex()função que criamos anteriormente. Removeremos a no-jsclasse que adicionamos em nosso HTML e usamos para criar um fallback caso o JavaScript não carregue.

Estamos quase terminando!

Criamos anteriormente uma função para verificar se os painéis de guias possuem elementos focalizáveis: determineTabIndex(); essa função precisa ser adicionada lá.

Você se lembra da nossa setSelectedTab()função? Precisamos adicionar uma pequena linha para remover a visibleclasse caso ela tenha sido usada. Para fazer isso, adicionaremos o código TABPANELS[0].classList.remove("visible");antes de começarmos a verificar cada guia.

Por fim, vamos usar o seguinte comando para garantir que nossa função handleInitialState()seja executada quando o site for carregado:

window.onload = handleInitialState;

Lembre-se, esta seção só se aplica se você usou o <a>elemento para manipular a navegação das guias.

Empacotando

Abaixo está nossa interface com guias acessível completa! Depois de considerar várias opções, usei a abordagem baseada em link com o <a>elemento para esta demonstração. A interface do usuário com guias foi projetada com um layout totalmente flexível, sem necessidade de consultas de mídia.

https://codepen.io/ItsCrisDiaz/pen/LYQjGpW

Este componente aborda os principais problemas de acessibilidade que os desenvolvedores encontram ao construir uma interface com guias: garantir que funcione corretamente para usuários de teclado e que as informações sejam mostradas corretamente para usuários de leitores de tela.

Construir um componente acessível pode ser um desafio devido às várias nuances dos requisitos de acessibilidade, mas é importante garantir que seu site seja utilizável para todos.

 Fonte: https://blog.logrocket.com/build-accessible-user-interface-tabs-javascript/

#javascript 

What is GEEK

Buddha Community

Crie Guias De interface Do Usuário Acessíveis Em JavaScript

Crie Guias De interface Do Usuário Acessíveis Em JavaScript

As guias de interface são um componente comum do site, mas os desenvolvedores geralmente enfrentam desafios para tornar esse padrão acessível. Os usuários de leitores de tela podem encontrar alguns problemas ao ler o conteúdo, e os usuários que dependem de um teclado para navegar em um site podem precisar de assistência para facilitar a navegação.

Este artigo cobrirá tudo o que você precisa saber para criar uma interface de guia acessível, incluindo a construção da estrutura HTML, a adição do estilo CSS e a adição de aprimoramentos da funcionalidade JavaScript.

Aqui está a interface com guias que construiremos neste tutorial:

Interface com abas finalizada

Uma interface com guias listando diferentes países. À esquerda, as abas são exibidas verticalmente e à direita, há um texto fictício e uma imagem para o país selecionado (neste caso, Mongólia).

Vamos começar!

Estabelecendo a estrutura HTML

A estrutura HTML é muito importante porque fornece aos usuários de leitores de tela o contexto necessário para navegar na interface.

Existem duas maneiras de usar a estrutura HTML para ajudar a definir quais elementos podem ser focados com um teclado. Podemos usar uma abordagem baseada em link com o <a>elemento como nosso gatilho de guia ou podemos usar o <button>elemento.

Ambas as abordagens levam em consideração a navegação pelo teclado por serem nativamente focáveis ​​pelo teclado, mas cada uma tem vantagens e desvantagens. Abordaremos ambas as abordagens neste artigo.

Usando o <a>elemento como a guia

A principal vantagem dessa abordagem é que podemos usar a <a>funcionalidade do elemento para criar o efeito de navegação da guia usando apenas HTML e CSS. O <a>elemento traz suporte ao teclado cuidando muito do gerenciamento de foco e navegação para usuários de leitores de tela para nós!

Essa abordagem também é útil para usuários que têm o JavaScript desabilitado. Usaremos JavaScript apenas no final deste tutorial — para aprimorar algumas das <a>funcionalidades do elemento para fins de acessibilidade.

No entanto, uma pequena desvantagem de usar o <a>elemento como guia é que ele é contado no histórico de navegação do usuário. Em outras palavras, se o usuário visitar todas as abas e depois quiser retornar ao site anterior, terá que pressionar o botão voltar várias vezes.

Ainda assim, essa abordagem tem aprimoramento progressivo em mente, pois oferece a maior funcionalidade de interface de guia com a menor tecnologia possível. Essa abordagem funcionará mesmo que o JavaScript não carregue e mesmo que o CSS também falhe, então vale a pena considerar.

Usando o <button>elemento como a guia

Usar o <button>elemento como guia tem a vantagem de não afetar o histórico de navegação, facilitando o retorno do usuário ao site anterior.

No entanto, essa abordagem requer algumas considerações extras com HTML para ocultar todo o conteúdo de guias não ativas. Também requer algumas decisões adicionais em relação ao JavaScript, como determinar como exibir conteúdo, estratégias de gerenciamento de foco e alguns aprimoramentos de acessibilidade.

Se você usar essa abordagem, precisará considerar o que acontecerá se o JavaScript não carregar em seu site. Sem um fallback, os usuários não poderão navegar em sua interface com guias. Existem várias razões pelas quais o JavaScript pode falhar ao carregar. Por exemplo, os usuários podem ter o JavaScript desabilitado devido a questões de rastreamento e privacidade, ou o JavaScript pode falhar ao carregar devido a fatores externos.

Criando a estrutura da guia da interface

Depois de decidir qual abordagem você usará, é hora de começar a criar a estrutura HTML!

Estrutura da interface da guia com a <a>abordagem do elemento

Vamos começar com as guias individuais. Aqui, usaremos a <a>abordagem de elemento para a estrutura HTML:

<!-- Tab semantics -->
<ul role="tablist">
  <li role="presentation">
    <a role="tab" href="#panel1" id="tab1">Tab one</a>
  </li>
  <li role="presentation">
    <a role="tab" href="#panel2" id="tab2">Tab two</a>
  </li>
  <li role="presentation">
    <a role="tab" href="#panel3" id="tab3">Tab three</a>
  </li>
  <li role="presentation">
    <a role="tab" href="#panel4" id="tab4">Tab four</a>
  </li>
</ul>

<!-- Tab content semantics -->
<div class="tabpanel-container">
  <section role="tabpanel" id="panel1" aria-labelledby="tab1" tabindex="0"></section>
  <section role="tabpanel" id="panel2" aria-labelledby="tab2" tabindex="0"></section>
  <section role="tabpanel" id="panel3" aria-labelledby="tab3" tabindex="0"></section>
  <section role="tabpanel" id="panel4" aria-labelledby="tab4" tabindex="0"></section>
</div>

Agora, vamos examinar a estrutura mostrada no código acima:

  • No final do dia, uma lista de guias é apenas uma lista de links (ou botões) que nos levarão ao nosso conteúdo, então a semântica correta para a lista de guias é envolvê-la com nosso elemento selecionado ( <a>ou <button>) dentro da lista itens dentro de um <ul>elemento
  • Adicionamos a tablistfunção ao nosso <ul>para fornecer mais contexto para usuários de leitores de tela; essa função é usada para marcar um contêiner que envolve um conjunto de guias. Como o tablistpapel não fornece a semântica de tabulação ao elemento filho, adicionamos o tabpapel ao nosso <a>elemento
  • Se um usuário de leitor de tela não conseguir ler as funções tabliste tab, o HTML ainda será lido como uma lista de links ou botões, o que é um substituto aceitável
  • Adicionamos o presentationpapel aos <li>elementos apenas para remover a semântica; isso ajudará a evitar interações estranhas, mantendo nosso fallback caso um leitor de tela específico não ofereça suporte a funções ARIA
  • Nosso conteúdo da guia será representado por um <section>elemento com o tabpanelpapel. Espera-se que esse elemento use o mesmo nome da guia como um nome acessível. É por isso que adicionamos o idatributo às nossas guias e o estamos usando como um rótulo em nossos painéis de guias com o aria-labelledbyatributo
  • Adicionamos o atributo tabindex="0"aos nossos painéis de guias para permitir que itens dentro da guia (como campos de formulário, links ou botões) recebam o foco do teclado. Isso tornará mais fácil para os usuários de teclado acessar o conteúdo

Estrutura da interface da guia com a <button>abordagem do elemento

Se usarmos a <button>abordagem baseada em -, precisaremos incluir uma etapa extra. Precisaremos adicionar o hiddenatributo a todos os painéis de guias, exceto a primeira guia. Este atributo ocultará os elementos para usuários com visão e usuários de leitores de tela. Para garantir que a primeira guia permaneça visível, ela deve ter o aria-selected="true"atributo.

Nossa marcação para a <button>abordagem deve ser algo assim:

<!-- Tab semantics -->
<ul role="tablist">
  <li role="presentation">
    <button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel2" id="tab2">Tab two</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel3" id="tab3">Tab three</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel4" id="tab4">Tab four</button>
  </li>
</ul>

<!-- Tab content semantics -->
<div class="tabpanel-container">
  <section role="tabpanel" id="panel1" aria-labelledby="tab1" tabindex="0"></section>
  <section role="tabpanel" id="panel2" aria-labelledby="tab2" tabindex="0" hidden></section>
  <section role="tabpanel" id="panel3" aria-labelledby="tab3" tabindex="0" hidden></section>
  <section role="tabpanel" id="panel4" aria-labelledby="tab4" tabindex="0" hidden></section>
</div>

Considerações adicionais sobre a estrutura HTML

Até agora, cobrimos as considerações iniciais para nossa marcação, mas há fatores adicionais que precisamos ter em mente para determinados casos de uso. Alguns fatores serão controlados de forma mais dinâmica, mas falaremos sobre eles um pouco mais adiante na seção JavaScript deste artigo.

Relacionando abas com o painel de abas

Podemos relacionar as guias com o painel de guias para usuários de leitores de tela usando o atributo aria-controls. No entanto, essa abordagem funcionará apenas no leitor de tela JAWS e seu uso pode parecer bastante detalhado, conforme explicado no artigo de Heydon Pickering . Em vez disso, usaremos algumas estratégias de gerenciamento de foco que também ajudarão os usuários de teclado.

Manipulando guias verticais

Para exibir uma lista de guias com orientação vertical, você precisará adicionar o atributo [aria-orientation="vertical"]ao contêiner. Isso indicará aos usuários de leitores de tela que as guias estão empilhadas verticalmente.

Isso será importante quando começarmos a discutir estratégias de navegação para nosso exemplo de interface com guias.

Aqui está um exemplo mostrando como o [aria-orientation="vertical"]atributo seria usado em um snippet de código:

<ul role="tablist" aria-orientation="vertical">
  <li role="presentation">
    <button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel2" id="tab2">Tab two</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel3" id="tab3">Tab three</button>
  </li>
  <li role="presentation">
    <button role="tab" href="#panel4" id="tab4">Tab four</button>
  </li>
</ul>

Indicando a seleção da guia

Quando um usuário clicar em uma guia, precisaremos de uma maneira de indicar que ela foi selecionada.

Usaremos o atributo [aria-selected="true"]para indicar a seleção de guias para usuários de leitores de tela. Em seguida, vamos estilizá-lo com CSS para ajudar a indicar a seleção para usuários com visão. O [aria-selected="true"]atributo mudará dinamicamente com JavaScript, portanto, não será adicionado à nossa marcação inicial.

<li role="presentation">
  <button role="tab" href="#panel1" id="tab1" aria-selected="true">Tab one</button>
</li>

Agora que temos uma estrutura HTML sólida, é hora de adicionar algum estilo com CSS !

Adicionando estilo e layout com CSS

Para nosso exemplo de interface com guias, estamos usando uma abordagem baseada em link com o <a>elemento como nosso gatilho de guia. Para estilizar a estrutura de guias HTML, precisaremos adicionar a navegação de guias, criar o indicador de foco e identificar o estilo do estado selecionado.

Adicionando a navegação da guia

Para adicionar a navegação da guia, vamos adicionar uma regra CSS usando o :targetseletor assim:

.tabpanel:not(:target):not(.visible) {
  display: none;
}

:targeté uma pseudo-classe que verifica se um elemento com um idcorresponde ao fragmento da URL.

Como estamos usando o <a>elemento aqui para adicionar funcionalidade, podemos usar esse seletor para ocultar quaisquer painéis de guias não ativos. Estamos usando a visibleclasse como uma exceção em nosso exemplo para que possamos adicionar alguns aprimoramentos com JavaScript posteriormente .

Criando o indicador de foco

Outra coisa que precisamos ter em mente é a navegação pelo teclado, e uma das coisas mais importantes a fazer nesse sentido é criar um indicador de foco adequado.

Você provavelmente notou um indicador de foco ao navegar em um site usando a tecla tab . Dependendo do seu navegador, aparecerá como um contorno azul ou preto destacando um elemento selecionado.

Os navegadores oferecem estilos de foco padrão, mas não são suficientes para atender aos critérios WCAG 2.2 . Portanto, é necessário usar um estilo adicional. Para obter informações mais detalhadas sobre como criar um bom indicador de foco, leia o artigo de Sara Souiedan sobre este tópico.

Para nosso exemplo, usaremos os seguintes estilos de foco:

.tab:focus-visible {
  background-color: royalblue;
  color: whitesmoke;
  outline: 0.2em solid transparent;
}

Decidi usar :focus-visibleem vez da :focuspseudo-classe para que o estilo de foco seja ativado apenas com a navegação do teclado. A :focuspseudo-classe também será ativada quando o elemento for clicado, o que pode ser confuso para os usuários.

:focus-visibleé muito bem suportado em navegadores modernos, por isso não deve criar nenhum tipo de conflito. Para obter mais informações sobre essa propriedade, consulte a documentação do MDN .

A opção “Mongólia” selecionada abaixo mostra como ficaria o estado de foco da nossa guia:

Guia Mongólia

A primeira guia na lista de guias do projeto tem um fundo azul, fornecendo uma dica visual para a seleção de guias para usuários de teclado.

Para este projeto, decidi usar um contorno transparente. Isso é importante para o modo de alto contraste do Windows . Nesse modo, todas as cores do texto e do plano de fundo são substituídas pelas escolhidas pelo sistema operacional, portanto, não podemos depender da cor do plano de fundo para indicar um estado de foco. O contorno é a única maneira confiável de indicar o estado do foco.

Identificando o estilo do estado selecionado

Agora, vamos voltar ao que mencionei anteriormente sobre o uso do aria-selected="true"atributo para ajudar os usuários de leitores de tela a identificar mais facilmente a guia selecionada. Podemos usar esse seletor para fornecer uma dica visual para a guia selecionada também!

.tab[aria-selected="true"] {
  background-color: var(--component-bg);
}

Essa abordagem cria um pequeno problema devido ao funcionamento da especificidade do CSS. Ambos os seletores .tab:focus-visiblee .tab[aria-selected="true"], têm o mesmo nível de especificidade e ambos alteram a background-colorpropriedade, portanto, a ordem da regra do seletor é crítica.

Queremos que o estado de foco background-colorsobrescreva qualquer outro background-color, incluindo o estado selecionado, então adicionaremos a .tab:focus-visibleregra seletora após a .tab[aria-selected="true"]regra seletora.

Aqui está o nosso resultado; observe como os dois estilos interagem entre si sem nenhum problema de especificidade:

Guia de Mianmar

A primeira guia na lista de guias do projeto está ativada e tem um fundo cinza, enquanto a terceira guia está focada no teclado e tem um fundo azul.

Se você estiver usando o <a>elemento como sua guia, você já criou uma interface de guia funcional com a menor tecnologia possível!

Se você usou a <button>abordagem baseada em -, não se preocupe! Em seguida, adicionaremos algumas funcionalidades com JavaScript, que serão cruciais para tornar a interface com abas mais acessível.

Adicionando melhorias e funcionalidades com JavaScript

Há muito para descompactar nesta seção; vamos começar verificando o que o ARIA Authoring Practices Guide (APG) tem a dizer sobre esse padrão de componente. Aqui está o que o guia de padrões do ARIA APG para a interface de guia considera importante para a tecla tabtabindex , os elementos in tabpanele as teclas de seta .

Gerenciando a tecla tab

Em relação ao uso da tecla tab , o ARIA APG sugere o seguinte :

Quando o foco se move para a lista de guias, coloque o foco no tabelemento ativo.

Nossa primeira tarefa é remover a navegação do teclado com a tecla tab para a guia não selecionada para que, quando a tecla for pressionada, o foco vá diretamente para o painel de guias ativo. Então, quando shift + tab é pressionado no painel ativo, o foco volta para sua respectiva aba. Além disso, teremos que indicar a seleção de guias para usuários de leitores de tela e usuários com visão.

Aqui está o código que usei para resolver essas tarefas:

const TABLIST = document.querySelector("#tablist");
const TABS = [...TABLIST.querySelectorAll(".tab")];

const setSelectedTab = (element) => {
  const selectedId = element.id;

  TABS.forEach((e) => {
    const id = e.getAttribute("id");
    if (id === selectedId) {
      e.removeAttribute("tabindex");
      e.setAttribute("aria-selected", "true");
    } else {
      e.setAttribute("tabindex", "-1");
      e.setAttribute("aria-selected", "false");
    }
  });
};

Aqui, selecionamos todas as abas e armazenamos aquela que está sendo clicada. Em seguida, analisamos o array com todas as nossas guias e comparamos cada idatributo para verificar se é a guia que foi selecionada.

Se a guia não for a que desejamos selecionar, adicionamos os atributos tabindex="-1"e . aria-selected="false"No entanto, se a guia for a que desejamos selecionar, removemos o tabindexatributo e adicionamos o aria-selected="true"atributo. Os elementos <a>e <button>são focados no teclado por padrão, portanto, não há necessidade de adicionar um arquivo tabindex="0".

O tabindex="-1"atributo tornará um elemento não focalizável com a tecla tab . Quando a tecla é pressionada, ela move o foco diretamente para o nosso painel de guias ativo, permitindo pular todas as nossas guias. Isso também é importante porque também nos permitirá gerenciar o foco das guias em outra que demonstrarei um pouco mais adiante neste artigo .

Agora tudo o que precisamos fazer é adicionar ouvintes de eventos às nossas guias para executar esta função! Para uma abordagem baseada em link, precisaremos adicionar um ouvinte de evento adicional a esses elementos.

Com uma <button>abordagem baseada, qualquer evento de clique será ativado pelas teclas enter e espaço . No entanto, um <a>elemento só adicionará esses eventos com a tecla enter , portanto, precisaremos adicionar um ouvinte de evento keydown para verificar quando a tecla de espaço é pressionada.

const handleClick = () => {
  TABS.forEach((element) => {
    element.addEventListener("click", function () {
      setSelectedTab(element);
    });
  });

  // Activates tab with Space. Only necessary if you use the <a> element as a tab

  TABS.forEach((element) => {
    element.addEventListener("keydown", function (e) {
      if ((e.keyCode || e.which) === 32) {
        setSelectedTab(element);
        element.click();
      }
    });
  });
};

Aqui está o nosso resultado!

Navegação de guias verticais

Projeto de amostra com navegação de guia vertical. Quando a tecla tab é pressionada, ela move o foco do teclado para o painel de guias; quando shift+tab é pressionado, ele move o foco para a guia selecionada.

Gerenciando os elementos tabindexintabpanel

O ARIA APG recomenda o seguinte para a interação do teclado da lista de guias :

Quando a lista de guias contém o foco, [tab] move o foco para o próximo elemento na sequência de guias da página fora da lista de guias, que é o painel de guias, a menos que o primeiro elemento contendo conteúdo significativo dentro do painel de guias seja focalizável.

Com base nessa recomendação, nossa próxima tarefa é verificar se cada painel de guia contém um elemento focalizável.

Para painéis de guias que não contêm um elemento focalizável, manteremos o tabindex="0"atributo que adicionamos anteriormente. Caso contrário, atualizaremos o atributo para tabindex="-1", portanto, quando a tecla tab for pressionada, o foco será movido para o primeiro elemento focalizável dentro do painel de guias.

Usaremos este snippet de código para verificar se cada painel de guia contém elementos focalizáveis ​​e alterar o tabindexatributo conforme necessário:

const TABPANELS = [...document.querySelectorAll(".tabpanel")];

const determineTabindex = () => {
  TABPANELS.forEach((element) => {
    const focusableElements = element.querySelectorAll(
      'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]):not([disabled]), details:not([disabled]), summary:not(:disabled)'
    ).length;

    focusableElements
      ? element.setAttribute("tabindex", "-1")
      : element.setAttribute("tabindex", "0");
  });
};

Aqui está o nosso resultado!

Navegação do teclado

Interface com guias com navegação por teclado. Quando um painel de guias tem um elemento focalizável e navegamos usando a tecla tab, o foco é movido para o primeiro elemento focalizável. Caso contrário, o foco estará no próprio painel de guias.

Neste exemplo, quando tentamos usar nosso teclado para navegar na interface, o elemento focado varia dependendo se o painel de guias contém um elemento focado no teclado.

Gerenciando a navegação com as teclas de seta

O ARIA APG oferece recomendações específicas de navegação pelo teclado, dependendo da orientação das guias da interface.

Aqui estão as recomendações quando o foco está em um elemento de guia em uma lista de guias horizontais :

seta para a esquerda : Move o foco para a guia anterior. Se o foco estiver na primeira guia, mova o foco para a última guia. Opcionalmente, ativa a guia recém-focada.
seta para a direita : Move o foco para a próxima guia. Se o foco estiver no último elemento da guia, mova o foco para a primeira guia. Opcionalmente, ativa a guia recém-focada.

Se você estiver construindo uma interface com abas exibidas verticalmente, além de usar o atributo aria-orientation="vertical", você terá que usar as teclas de seta para cima e seta para baixo em vez das teclas de seta para a esquerda e seta para a direita .

Aqui estão as recomendações quando uma lista de guias tem orientação de ária definida como vertical :

Quando uma lista de guias tem sua orientação de ária definida como vertical:
a seta para baixo funciona como a seta para a direita descrita acima.
seta para cima funciona como a seta para a esquerda é descrita acima.

Minha intenção para este projeto era construir uma interface completamente flexível, mas com essa abordagem em algum momento a tela acabará sendo uma lista de abas horizontais ou uma lista de abas verticais. Como abordamos isso?

Vamos começar determinando quais teclas de seta devemos usar. Neste caso específico, temos duas alternativas:

  1. Verifique a largura e a altura da lista de guias. Se a altura for maior que a largura, adicione o atributo aria-orientation="vertical"e dê suporte apenas à navegação de seta para cima e seta para baixo. Caso contrário, adicione o atributo e dê suporte apenas à navegação de seta para a esquerda e seta para a direita .aria-orientation="horizontal"
  2. Apoie todas as quatro teclas de navegação ao mesmo tempo. Se o usuário pressionar a seta para baixo ou a seta para a direita , ele moverá o foco para a próxima guia. Se eles pressionarem a seta para a esquerda ou a seta para cima , ele moverá o foco para a guia anterior.

Eu geralmente prefiro a solução mais simples, então escolho a segunda opção. No entanto, cada projeto é diferente. Se sua lista de guias estiver sempre na posição vertical, é melhor usar aria-orientation="vertical"e oferecer suporte apenas à navegação com seta para cima e seta para baixo .

Aqui está o código que usei, com base na segunda opção:

const createArrowNavigation = () => {
  const firstTab = TABS[0];
  const lastTab = TABS[TABS.length - 1];

  TABS.forEach((element) => {
    element.addEventListener("keydown", function (e) {
      if ((e.keyCode || e.which) === 38 || (e.keyCode || e.which) === 37) {
        if (element == firstTab) {
          e.preventDefault();
          lastTab.focus();
        } else {
          e.preventDefault();
          const focusableElement = TABS.indexOf(element) - 1;
          TABS[focusableElement].focus();
        }
      } else if (
        (e.keyCode || e.which) === 40 ||
        (e.keyCode || e.which) === 39
      ) {
        if (element == lastTab) {
          e.preventDefault();
          firstTab.focus();
        } else {
          e.preventDefault();
          const focusableElement = TABS.indexOf(element) + 1;
          TABS[focusableElement].focus();
        }
      }
    });
  });
};

Anteriormente, criamos uma variável que armazena todas as nossas guias. Podemos usá-lo para determinar qual é a primeira e a última guia de navegação. Isso é importante porque essas guias têm um comportamento especial.

Em seguida, podemos verificar quais códigos de tecla estão relacionados a cada tecla de seta. Para isso, podemos usar o site keycode.info para conferir. Para facilitar o processo, aqui está a lista dos códigos-chave:

ChaveCódigo chave
Seta esquerda37
Seta para cima38
Seta direita39
Seta para baixo40

Agora, ouvimos cada tecla para verificar a posição da guia focada no array e focamos no próximo elemento (a seta para a direita e a seta para baixo ) ou o elemento anterior (a seta para a esquerda e a seta para cima ) no array com o focus()método . Como esses elementos têm o tabindex="-1"atributo, poderemos focalizá-los com esse método.

Tenha em mente que se o elemento for o primeiro no array, ele moverá o foco para o último elemento e vice-versa. Outro ponto a ser considerado é que as teclas de seta também têm a função de mover a barra de rolagem; podemos evitar esse comportamento com o e.preventDefault()método.

Aqui está nossa interface, mostrando o uso das teclas de seta para mover entre as abas:

Usando as teclas de seta

Interface com guias com navegação pelo teclado, usando as teclas de seta para mover entre as guias.

Determinando como mostrar o conteúdo

Além de gerenciar o estado de foco de nosso componente, precisamos determinar o tempo para ativar a guia recém-focada. Em outras palavras, quando o painel de guias deve ser exibido? Deve ser mostrado quando a guia é clicada ou quando o foco é colocado na guia selecionada?

A resposta a este tópico é surpreendentemente matizada, e o W3C tem uma seção inteira sobre este tópico . Para resumir o W3C, alterar o conteúdo exibido no momento de focar um elemento (chamado de follow focus) pode ser benéfico para certos componentes, mas cria erros de acessibilidade para outros.

Exibindo conteúdo no momento do clique

A exibição de conteúdo no momento do foco pode facilitar a navegação pelo teclado para usuários com visão, mas os usuários de leitores de tela podem não estar cientes de que um novo conteúdo foi adicionado à página. Além disso, precisamos considerar a quantidade de conteúdo que será exibida, pois isso pode afetar o desempenho.

Sugiro exibir o conteúdo quando a guia for clicada. Essa opção pode exigir um pressionamento extra de teclas para usuários de teclado, mas fornecerá uma experiência mais inclusiva para usuários de leitores de tela.

Quando o <a>elemento é clicado, o foco é trazido para o painel de guias selecionado, que abrangerá a navegação para usuários de leitores de tela.

Exibindo conteúdo no momento do foco

Se você decidir exibir o conteúdo com base no foco, certifique-se de usar a <button>abordagem, em vez da <a>abordagem do elemento.

Com um <button>elemento, você precisará lidar com o gerenciamento de foco com JavaScript. Para isso, voltaremos à handleClickfunção que criamos anteriormente na seção CSS deste artigo e adicionaremos alguns ajustes.

Primeiro, criaremos uma função que adiciona ou remove o atributo hiddendependendo se o painel de guias está sendo direcionado. Se o painel de guias for o item de destino, também moveremos o foco do teclado para ele. Aqui está a minha abordagem para esta questão:

const showActivePanel = (element) => {
  const selectedId = element.id;
  TABPANELS.forEach((e) => {
    e.hidden = "true";
  });
  const activePanel = document.querySelector(
    `[aria-labelledby="${selectedId}"]`
  );
  activePanel.removeAttribute("hidden");
        activePanel.focus()
};

O que estamos fazendo aqui é ocultar todos os painéis de guias adicionando a eles o hiddenatributo. Em seguida, selecionaremos nosso painel de guias de destino usando o aria-labelledbyvalor do atributo. Lembre-se, cada buttonum tem um idque estamos usando para rotular o painel de guias usando este atributo; isso nos ajudará a selecionar o painel de guias correto.

Agora, apenas removemos o hiddenatributo e, em seguida, trazemos o foco do teclado para ele com o focus()método. Por fim, adicionamos a showActivePanelfunção em nossa handleClickfunção para ser executada no clique.

Agora é hora de ver como o componente funciona como um todo!

Clique em Navegação

Interface com abas mostrando o conteúdo no clique; as teclas de seta são usadas para navegação do teclado entre as guias.

Manipulando o <a>estado inicial do elemento

Se optarmos por usar um <a>elemento para nossas guias, precisaremos lidar com o estado inicial do elemento se o JavaScript estiver ativo.

Aqui estão as ações que queremos realizar quando o documento for carregado:

  • Se a URL do site não estiver apontando para os painéis de abas, o primeiro painel deve estar visível, a primeira aba deve ter o atributo aria-selected="true"e ser focável pelo teclado quando a tecla tab for pressionada; as outras abas devem ter aria-selected="false"e tabindex="-1"atributos
  • Se o URL do site estiver apontando para um painel, o painel nesse URL deverá estar visível; a respectiva aba deve ter os atributos mencionados anteriormente: aria-selected="true"eno tabindex

Vamos criar as funções para ambos os cenários.

const activateFirstPanel = () => {
  TABS[0].setAttribute("tabindex", "0");
  TABS[0].setAttribute("aria-selected", "true");
  TABPANELS[0].classList.add("visible");
};

Esta função cobrirá nosso primeiro caso. Na verdade é bem simples. Já temos as listas de nós TABSe TABPANELSnós, podemos selecionar o primeiro elemento de cada lista e adicionar as propriedades necessárias.

Na seção CSS anterior, mencionei que estávamos usando a visibleclasse como uma exceção e adicionaríamos alguns aprimoramentos com JavaScript posteriormente. Bem, agora é o momento para os aprimoramentos do JavaScript!

Adicionamos essa classe ao primeiro painel de guias para torná-la visível. Assim que o usuário começar a interagir com a guia, precisaremos remover essa classe.

const checkInitialSelectedTab = () => {
  const targetedTabPanel = document
    .querySelector(".tabpanel:target")
    .getAttribute("aria-labelledby");
  const selectedTab = document.querySelector(`#${targetedTabPanel}`);
  selectedTab.setAttribute("aria-selected", "true");
  selectedTab.removeAttribute("tabindex");
};

Nosso segundo cenário é um pouco mais complicado. Como este painel está sendo direcionado, ele será exibido na tela por padrão - isso se deve ao seletor
.tabpanel:not(:target):not(.visible)que usamos anteriormente. Mas, precisamos procurar a aba que ativa o painel.

Para isso, selecionaremos o painel de guias direcionado com JavaScript usando o .tabpanel:targetseletor no querySelector()método. Assim que tivermos esse nó, obteremos o aria-labelledbyatributo. A guia idé a mesma que estamos usando no aria-labelledbyatributo deste painel, então usaremos isso idpara procurar a guia e adicionaremos os atributos necessários.

Agora, só precisamos executar essas funções dependendo do que a URL contém, então usaremos outra função para lidar com isso:

const handleInitialState = () => {
  TABS.forEach((e) => {
    e.setAttribute("tabindex", "-1");
    e.setAttribute("aria-selected", "false");
  });

  window.location.href.indexOf("#panel") === -1
    ? activateFirstPanel()
    : checkInitialSelectedTab();

  determineTabindex();
};

Bem, eu menti. Há apenas um pouco mais de trabalho que precisamos fazer nesta função.

Para começar, vamos definir o aria-selectedatributo de todas as guias para falsee o tabindexatributo para -1; então vamos corrigir o correto com as funções criadas.

Em seguida, precisamos verificar se um painel de guias está sendo segmentado na URL do site. Podemos usar o window.location.hrefmétodo para obter a URL e, em seguida, usar o indexOfmétodo array para verificar se existe um painel direcionado a essa URL.

Quando você usa uma função array em uma string, como nossa URL, ela vai criar uma array com cada caractere da string como um elemento dessa array, se o mundo inteiro estiver nessa URL, ela vai retornar a posição inicial de a string, se não, retornará -1. Isso é o que usaremos para determinar qual função executar.

Agora, vamos executar a determineTabindex()função que criamos anteriormente. Removeremos a no-jsclasse que adicionamos em nosso HTML e usamos para criar um fallback caso o JavaScript não carregue.

Estamos quase terminando!

Criamos anteriormente uma função para verificar se os painéis de guias possuem elementos focalizáveis: determineTabIndex(); essa função precisa ser adicionada lá.

Você se lembra da nossa setSelectedTab()função? Precisamos adicionar uma pequena linha para remover a visibleclasse caso ela tenha sido usada. Para fazer isso, adicionaremos o código TABPANELS[0].classList.remove("visible");antes de começarmos a verificar cada guia.

Por fim, vamos usar o seguinte comando para garantir que nossa função handleInitialState()seja executada quando o site for carregado:

window.onload = handleInitialState;

Lembre-se, esta seção só se aplica se você usou o <a>elemento para manipular a navegação das guias.

Empacotando

Abaixo está nossa interface com guias acessível completa! Depois de considerar várias opções, usei a abordagem baseada em link com o <a>elemento para esta demonstração. A interface do usuário com guias foi projetada com um layout totalmente flexível, sem necessidade de consultas de mídia.

https://codepen.io/ItsCrisDiaz/pen/LYQjGpW

Este componente aborda os principais problemas de acessibilidade que os desenvolvedores encontram ao construir uma interface com guias: garantir que funcione corretamente para usuários de teclado e que as informações sejam mostradas corretamente para usuários de leitores de tela.

Construir um componente acessível pode ser um desafio devido às várias nuances dos requisitos de acessibilidade, mas é importante garantir que seu site seja utilizável para todos.

 Fonte: https://blog.logrocket.com/build-accessible-user-interface-tabs-javascript/

#javascript 

Rahul Jangid

1622207074

What is JavaScript - Stackfindover - Blog

Who invented JavaScript, how it works, as we have given information about Programming language in our previous article ( What is PHP ), but today we will talk about what is JavaScript, why JavaScript is used The Answers to all such questions and much other information about JavaScript, you are going to get here today. Hope this information will work for you.

Who invented JavaScript?

JavaScript language was invented by Brendan Eich in 1995. JavaScript is inspired by Java Programming Language. The first name of JavaScript was Mocha which was named by Marc Andreessen, Marc Andreessen is the founder of Netscape and in the same year Mocha was renamed LiveScript, and later in December 1995, it was renamed JavaScript which is still in trend.

What is JavaScript?

JavaScript is a client-side scripting language used with HTML (Hypertext Markup Language). JavaScript is an Interpreted / Oriented language called JS in programming language JavaScript code can be run on any normal web browser. To run the code of JavaScript, we have to enable JavaScript of Web Browser. But some web browsers already have JavaScript enabled.

Today almost all websites are using it as web technology, mind is that there is maximum scope in JavaScript in the coming time, so if you want to become a programmer, then you can be very beneficial to learn JavaScript.

JavaScript Hello World Program

In JavaScript, ‘document.write‘ is used to represent a string on a browser.

<script type="text/javascript">
	document.write("Hello World!");
</script>

How to comment JavaScript code?

  • For single line comment in JavaScript we have to use // (double slashes)
  • For multiple line comments we have to use / * – – * /
<script type="text/javascript">

//single line comment

/* document.write("Hello"); */

</script>

Advantages and Disadvantages of JavaScript

#javascript #javascript code #javascript hello world #what is javascript #who invented javascript

Hire Dedicated JavaScript Developers -Hire JavaScript Developers

It is said that a digital resource a business has must be interactive in nature, so the website or the business app should be interactive. How do you make the app interactive? With the use of JavaScript.

Does your business need an interactive website or app?

Hire Dedicated JavaScript Developer from WebClues Infotech as the developer we offer is highly skilled and expert in what they do. Our developers are collaborative in nature and work with complete transparency with the customers.

The technology used to develop the overall app by the developers from WebClues Infotech is at par with the latest available technology.

Get your business app with JavaScript

For more inquiry click here https://bit.ly/31eZyDZ

Book Free Interview: https://bit.ly/3dDShFg

#hire dedicated javascript developers #hire javascript developers #top javascript developers for hire #hire javascript developer #hire a freelancer for javascript developer #hire the best javascript developers

Niraj Kafle

1589255577

The essential JavaScript concepts that you should understand

As a JavaScript developer of any level, you need to understand its foundational concepts and some of the new ideas that help us developing code. In this article, we are going to review 16 basic concepts. So without further ado, let’s get to it.

#javascript-interview #javascript-development #javascript-fundamental #javascript #javascript-tips

Ajay Kapoor

1626321063

JS Development Company India | JavaScript Development Services

PixelCrayons: Our JavaScript web development service offers you a feature-packed & dynamic web application that effectively caters to your business challenges and provide you the best RoI. Our JavaScript web development company works on all major frameworks & libraries like Angular, React, Nodejs, Vue.js, to name a few.

With 15+ years of domain expertise, we have successfully delivered 13800+ projects and have successfully garnered 6800+ happy customers with 97%+ client retention rate.

Looking for professional JavaScript web app development services? We provide custom JavaScript development services applying latest version frameworks and libraries to propel businesses to the next level. Our well-defined and manageable JS development processes are balanced between cost, time and quality along with clear communication.

Our JavaScript development companies offers you strict NDA, 100% money back guarantee and agile/DevOps approach.

#javascript development company #javascript development services #javascript web development #javascript development #javascript web development services #javascript web development company