Pesquisar conteúdo oculto

As interfaces da Web geralmente ocultam conteúdo para melhorar a experiência do usuário, economizar espaço na tela ou aumentar a performance da página. Métodos tradicionais como display: none ou visibility: hidden ocultam o conteúdo visualmente, mas também o tornam completamente inacessível para leitores de tela e recursos do navegador, como "Localizar na página".

Para ocultar o conteúdo visualmente, mas ainda permitir que ele seja pesquisável pelos usuários e que seja vinculado por links diretos usando fragmentos do URL e links "Rolar para fragmento de texto", use o elemento HTML <details> ou o atributo hidden="until-found". O elemento <details> geralmente é recomendado porque é mais simples de implementar e manter, mas há alguns casos mais complexos em que o <details> não é suficiente e o hidden="until-found" é necessário.

Exemplo:

  • Se você quiser controle total sobre o estilo do mecanismo de exibição/ocultação.
  • Se os controles de interface para mostrar/ocultar o conteúdo estiverem em outra parte do DOM.
  • Se você não quiser ocultar o conteúdo depois que ele for mostrado.

Como fazer a implementação

O elemento <details> tem texto pesquisável e acessível por padrão, e nenhuma implementação especial é necessária. Se possível, use <details> em vez de hidden="until-found".

Se você precisar usar hidden="until-found", siga estas instruções:

  1. Aplique o atributo: adicione o atributo HTML hidden="until-found" diretamente aos elementos que contêm o conteúdo que deve ser ocultado.
  2. Sincronize o estado da interface: se a interface tiver estados relacionados que dependem da visibilidade do conteúdo (por exemplo, atualizar atributos ARIA, alternar classes CSS abertas/fechadas ou girar ícones de acordeão):
    • Você PRECISA adicionar um listener de eventos para o evento beforematch.
    • Registre o listener de eventos beforematch diretamente no elemento que contém o atributo hidden="until-found". Como o evento se propaga, você também pode usar a delegação de eventos registrando um único listener em um elemento pai (como um contêiner de guias) para gerenciar várias seções ocultas de uma só vez.
    • Dentro do listener de eventos, execute a lógica para sincronizar os elementos da interface relacionados (como fechar outras guias abertas ou mudar o estado de um botão ativar/desativar).

Exemplo de código

Meta: renderizar um contêiner oculto em que o conteúdo permanece invisível para o usuário até que ele pesquise uma palavra nessa seção.

Como usar o elemento <details>

<details>
  <summary>Click to expand</summary>
  <p>This content is visually hidden.</p>
</details>

Declarações mutuamente exclusivas

Ao lidar com regiões de conteúdo mutuamente exclusivas, como um acordeão exclusivo, use o elemento HTML nativo <details> com um atributo name compartilhado.

<div class="accordion-group">
  <!-- The name attribute creates an exclusive disclosure group -->
  <details class="disclosure" name="my-accordion">
    <summary>Section 1</summary>
    <p>Section 1 content</p>
  </details>

  <details class="disclosure" name="my-accordion" open>
    <summary>Section 2</summary>
    <p>Section 2 content</p>
  </details>

  <details class="disclosure" name="my-accordion">
    <summary>Section 3</summary>
    <p>Section 3 content</p>
  </details>
</div>

Como usar o atributo hidden="until-found"

<!-- The browser automatically removes hidden="until-found" upon a search match -->
<div class="hidden-container" hidden="until-found">
  <p>This content is visually hidden.</p>
</div>

Declarações personalizadas mutuamente exclusivas

Ao lidar com regiões personalizadas mutuamente exclusivas controladas por botões externos, garanta que apenas o painel correspondente permaneça visível. No manipulador de eventos beforematch, você PRECISA sincronizar os estados ARIA relacionados, como definir aria-expanded="true" no botão de controle.

<div class="custom-accordion">
  <div class="controls">
    <button aria-expanded="true" aria-controls="panel-1" id="btn-1">Section 1</button>
    <button aria-expanded="false" aria-controls="panel-2" id="btn-2">Section 2</button>
  </div>

  <div id="panel-1" class="panel">
    <p>Section 1 content (visible)</p>
  </div>

  <div id="panel-2" class="panel" hidden="until-found">
    <p>Section 2 content (hidden)</p>
  </div>
</div>
const accordion = document.querySelector('.custom-accordion');

accordion.addEventListener('beforematch', (e) => {
  // Hide all panels and synchronize button states before the browser reveals the matched panel
  accordion.querySelectorAll('.panel').forEach((panel) => {
    if (panel !== e.target) {
      panel.hidden = 'until-found';
    }
  });
  accordion.querySelectorAll('button').forEach((btn) => {
    const controls = btn.getAttribute('aria-controls');
    btn.setAttribute('aria-expanded', controls === e.target.id ? 'true' : 'false');
  });
});

Práticas recomendadas para hidden="until-found"

  • É RECOMENDÁVEL aplicar bordas, padding e planos de fundo a wrappers filhos aninhados, em vez de diretamente ao elemento com o atributo hidden="until-found". Isso evita mudanças de layout não intencionais ou resquícios visuais enquanto o elemento está oculto.
  • NÃO aplique display: none, visibility: hidden ou qualquer propriedade CSS display ou visibility associada diretamente a elementos com o atributo hidden="until-found". Isso interrompe a funcionalidade nativa e oculta permanentemente o conteúdo do índice da Pesquisa.
  • NÃO use hidden="until-found" para informações sensíveis, tokens de dados internos ou dados irrelevantes que não devem ser expostos pela pesquisa.
  • NÃO use hidden="until-found" como substituto para o texto "somente leitor de tela" (.sr-only).

Suporte ao navegador e estratégias de substituição

NÃO evite o uso de hidden="until-found" devido à falta de suporte do navegador, já que os benefícios de acessibilidade superam o custo de implementar um substituto.

hidden="until-found" de substituição

Para elementos da interface padrão, como acordeões ou seções "Leia mais", use o JavaScript para detectar recursos e mostrar todo o conteúdo se o recurso não for compatível.

if (!('onbeforematch' in HTMLElement.prototype)) {
  // Expand all hidden content for unsupported browsers
  document.querySelectorAll('[hidden="until-found"]').forEach((el) => {
    el.removeAttribute('hidden');
    // MANDATORY: also update any aria references to this element.
  });
}

Para paradigmas de interface mutuamente exclusivos (como painéis exclusivos personalizados em que o conteúdo compartilha a mesma região visual), o substituto precisa extrair e mostrar todo o conteúdo de forma linear abaixo da área interativa principal, usando fragmentos de âncora de URL para permitir que os usuários naveguem diretamente para as respectivas seções.