Logo

dev-resources.site

for different kinds of informations.

Construindo um show/hide disclosure component acessível com vanilla JS

Published at
12/4/2023
Categories
javascript
a11y
programming
portugues
Author
dougsource
Author
10 person written this
dougsource
open
Construindo um show/hide disclosure component acessível com vanilla JS

Nota: apenas traduzi o texto abaixo e postei aqui.

Um disclosure component é o nome formal do pattern em que você clica em um botão para revelar ou ocultar o conteúdo.

Isso inclui coisas como uma interação "mostrar mais/mostrar menos" para algum texto descritivo abaixo de um vídeo do YouTube ou um menu de hambúrguer que é revelado e ocultado quando você clica nele.

Veremos agora uma maneira de construir disclosure component de forma acessível.

O HTML inicial

Começamos com algum HTML básico.

Incluiremos uma div para nosso conteúdo, com um ID #now-you-see-me. Também adicionaremos um botão que podemos usar para alternar a visibilidade do conteúdo.

<button>Mostrar mais</button>

<div id="now-you-see-me">
    Agora você não vê.
</div>
Enter fullscreen mode Exit fullscreen mode

Por razões semânticas, o "toggle" deve ser sempre um botão.

Links implicam para os dispositivos assistivos que clicar neles o levará a algum lugar, enquanto os botões implicam que alguma interatividade é acionada.

Adicionando interatividade

A primeira coisa que precisamos fazer é "listen" quando o botão é clicado, para podermos alternar a visibilidade do nosso conteúdo.

Para começar, vamos usar a event delegation para "listen" eventos de clique.

document.addEventListener('click', function (event) {
    // Faça coisas...
});
Enter fullscreen mode Exit fullscreen mode

Somente execute em disclosure buttons

No momento, estamos detectando todos os cliques no document. Precisamos de uma maneira de filtrar todos os cliques que não estejam em nosso toggle button.

Vamos adicionar o atributo data-disclosure ao nosso botão e filtrar os cliques em qualquer elemento que não tenha esse atributo.

<button data-disclosure>Show More</button>
Enter fullscreen mode Exit fullscreen mode
document.addEventListener('click', function (event) {
    // Execute apenas em elementos que possuem o atributo [data-disclosure]
    // Se event.target não tiver o atributo, return encerra a callback function
    if (!event.target.hasAttribute('data-disclosure')) return;

    console.log('deu match!');
});
Enter fullscreen mode Exit fullscreen mode

Obtendo nosso conteúdo

Também precisamos de uma forma de associar nosso botão ao conteúdo correspondente. Podemos usar o atributo [aria-controls] para isso.

O atributo [aria-controls] informa aos screen readers que um botão controla o comportamento de outro conteúdo. Você atribui o ID desse conteúdo como o valor do atributo.

<button data-disclosure aria-controls="now-you-see-me">Mostrar mais</button>
Enter fullscreen mode Exit fullscreen mode

Em nosso event listener, podemos obter o valor do atributo aria-controls e passá-lo para document.querySelector() para obter nosso conteúdo.

Precisaremos prefixá-lo com # porque é um seletor de ID.

document.addEventListener('click', function (event) {
    // Execute apenas em elementos que possuem o atributo [data-disclosure]
    // Se event.target não tiver o atributo, return encerra a callback function
    if (!event.target.hasAttribute('data-disclosure')) return;

    // Obtêm o conteúdo para toggle
    // Se nenhum conteúdo correspondente for encontrado, encerra
    // a function com return
    var content = document.querySelector('#' + event.target.getAttribute('aria-controls'));
    if (!content) return;

    console.log(content);
});
Enter fullscreen mode Exit fullscreen mode

Toggling a visibilidade de conteúdo

A última consideração sobre acessibilidade é que o botão também deve ter um atributo [aria-expanded]. Isso informa aos screen readers qual é o estado atual do conteúdo.

Se o atributo [aria-expanded] tiver o valor true, o conteúdo será expandido. Se tiver um valor false, o conteúdo será colapsado.

<button data-disclosure aria-controls="now-you-see-me" aria-expanded="true">Show More</button>
Enter fullscreen mode Exit fullscreen mode

Em nosso script, verificaremos qual é o valor do atributo [aria-expanded].

Se for true, adicionaremos a alteração para false e adicionaremos o atributo [hidden] ao nosso conteúdo para ocultá-lo. Caso contrário, mudaremos para true e removeremos o atributo [hidden] para revelá-lo.

Embora true e false sejam booleanos, o método getAttribute() os retorna como uma string. Precisaremos verificar 'true' como uma string em nosso código.

document.addEventListener('click', function (event) {
    // Execute apenas em elementos que possuem o atributo [data-disclosure]
    // Se event.target não tiver o atributo, return encerra a callback function
    if (!event.target.hasAttribute('data-disclosure')) return;

    // Obtêm o conteúdo para toggle
    // Se nenhum conteúdo correspondente for encontrado, encerra a function com return
    var content = document.querySelector(
        '#' + event.target.getAttribute('aria-controls')
    );
    if (!content) return;

    // Se o conteúdo está visível, esconda ele
    // Caso contrário, mostre ele
    if (event.target.getAttribute('aria-expanded') === 'true') {
        event.target.setAttribute('aria-expanded', false);
        content.setAttribute('hidden', '');
    } else {
        event.target.setAttribute('aria-expanded', true);
        content.removeAttribute('hidden');
    }
});

Enter fullscreen mode Exit fullscreen mode

Agora nosso conteúdo irá realmente aparecer e ocultar quando clicado!

Ocultando conteúdo por default

Atualmente, nosso conteúdo está visível para começar. Em uma application real, provavelmente desejaríamos que eles fossem ocultados por default e revelados quando o botão fosse clicado.

Podemos alterar o valor [aria-expanded] para false e adicionar o atributo [hidden] a todo o nosso conteúdo por default para que tudo seja collapsed desde o início.

<button data-disclosure aria-controls="now-you-see-me" aria-expanded="false">Mostrar mais</button>

<div id="now-you-see-me" hidden>
    Agora você não vê.
</div>
Enter fullscreen mode Exit fullscreen mode

Mas... antes que o JavaScript seja carregado e executado, os usuários não poderão expandir o conteúdo.

Você pode clicar no botão, mas nada acontecerá, o que é confuso. E se o arquivo JS falhar por algum motivo, eles nunca poderão visualizar o conteúdo.

Aprimoramento progressivo

Uma pequena pitada de aprimoramento progressivo resolverá isso.

Primeiro, removeremos o atributo [hidden] do nosso conteúdo, mas iremos adicioná-lo aos elementos do botão e manteremos o atributo [aria-expanded] definido como false.

<button
    data-disclosure
    aria-controls="now-you-see-me"
    aria-expanded="false"
    hidden
>Mostrar mais</button>

<div id="now-you-see-me">
    Agora você não vê.
</div>
Enter fullscreen mode Exit fullscreen mode

Agora, por default, o conteúdo ficará visível, mas o botão ficará oculto.

Quando o JavaScript carrega, queremos mostrar todos os botões e ocultar todo o conteúdo. Usaremos document.querySelectorAll() para obter todos os nossos elementos de botão, encontrar seu conteúdo correspondente e ocultá-lo.

Para melhor suporte do navegador, devemos converter nosso NodeList em um array antes de usar o método Array.forEach().

// Obtêm disclosure buttons
var disclosures = Array.prototype.slice.call(
    document.querySelectorAll('[data-disclosure]')
);

// Itera dentro deles com Array.forEach()
disclosures.forEach(function (disclosure) {
    // Obtêm o conteúdo associado com o button
    var content = document.querySelector(
        '#' + disclosure.getAttribute('aria-controls')
    );

    // Se não há conteúdo, não mostra o button
    if (!content) return;

    // Mostra o button e esconde o conteúdo
    disclosure.removeAttribute('hidden');
    content.setAttribute('hidden', '');
});
Enter fullscreen mode Exit fullscreen mode

E com isso, temos um disclosure component acessível e progressivamente aprimorado.

Fonte

Newsletter de Go Make Things

portugues Article's
30 articles in total
Favicon
Validação e Sanitização em Aplicações Web
Favicon
Golang básico - Comparação de Igualdade
Favicon
Guia de React re-render: tudo, tudo de uma vez
Favicon
Go + hot reload
Favicon
Como animar o scrolling para anchor links com CSS
Favicon
Como criar uma sticky navigation apenas com CSS
Favicon
Como usar requestAnimationFrame() com vanilla JS
Favicon
Como atualizar a URL do navegador sem refresh a página usando a vanilla JS History API
Favicon
Por que utilizar um elemento form quando submit campos com JavaScript?
Favicon
Quando um framework é melhor que a manipulação nativa do DOM
Favicon
Recursão com vanilla JavaScript
Favicon
Como usar async e await com vanilla JavaScript
Favicon
Construindo um show/hide disclosure component acessível com vanilla JS
Favicon
Eu <3 cascade!
Favicon
Você provavelmente não precisa de um DOMContentLoaded event em seu Javacript
Favicon
Formas de escrever Immediately Invoked Function Expression (IIFE) no JavaScript
Favicon
Event delegation e múltiplos seletores com vanilla JS
Favicon
Usando a propriedade CSS currentColor para contruir componentes extensíveis
Favicon
CurrentColor e SVGs
Favicon
WTF é gzipping (e como é diferente da minificação)?
Favicon
Como eu estruturo meus projetos vanilla JS
Favicon
Uma alternativa vanilla JS para o método moment.js timeFromNow()
Favicon
Recriando o método lodash partition() em vanilla JS
Favicon
Recriando o método lodash inRange() em vanilla JS
Favicon
Recriando o método lodash pull() em vanilla JS
Favicon
Como converter segundos para minutos e horas em vanila JS
Favicon
Gerando números randômicos com vanila JS
Favicon
Obtendo os meses formatados com vanila JS
Favicon
Como ser um desenvolvedor mais produtivo
Favicon
Como construir um site que carregue em menos de um segundo

Featured ones: