DEV Community

Cover image for Construindo um show/hide disclosure component acessível com vanilla JS
doug-source
doug-source

Posted on

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

Top comments (0)