Revisão de JavaScript e acessibilidade

Atributos comuns de acessibilidade ARIA

  • Atributo aria-expanded: Usado para transmitir o estado de um recurso de alternância (ou divulgação) para usuários de leitores de tela.
<button id="menuBtn" aria-expanded="false">Menu</button>

<script>
  const btn = document.getElementById("menuBtn");

  btn.addEventListener("click", () => {
    const expanded = btn.getAttribute("aria-expanded") === "true";
    btn.setAttribute("aria-expanded", String(!expanded));
  });
</script>
  • Atributo aria-haspopup: Este estado é usado para indicar que um elemento interativo acionará um elemento pop-up quando ativado. Você só pode usar o atributo aria-haspopup quando o pop-up tiver um dos seguintes papéis: menu, listbox, tree, grid ou dialog. O valor de aria-haspopup deve ser um desses papéis ou true, que é o mesmo que menu.
<button
  id="menubutton"
  aria-haspopup="menu"
  aria-controls="filemenu"
  aria-expanded="false"
>
  File
</button>

<ul
  id="filemenu"
  role="menu"
  aria-labelledby="menubutton"
  hidden
>
  <li role="menuitem" tabindex="-1">Open</li>
  <li role="menuitem" tabindex="-1">New</li>
  <li role="menuitem" tabindex="-1">Save</li>
  <li role="menuitem" tabindex="-1">Delete</li>
</ul>

<script>
  const button = document.getElementById("menubutton");
  const menu = document.getElementById("filemenu");

  button.addEventListener("click", () => {
    const expanded = button.getAttribute("aria-expanded") === "true";

    button.setAttribute("aria-expanded", String(!expanded));
    menu.hidden = expanded;
  });
</script>
  • Atributo aria-checked: Este atributo é usado para indicar se um elemento está no estado marcado. Ele é mais comumente usado ao criar caixas de seleção personalizadas, botões de rádio, interruptores e caixas de lista.
<div
  id="checkbox"
  role="checkbox"
  aria-checked="true"
  tabindex="0"
  style="
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
  "
>
  <span
    id="box"
    aria-hidden="true"
    style="
      width: 16px;
      height: 16px;
      border: 2px solid blue;
      background: blue;
      display: inline-block;
    "
  ></span>
  Checkbox
</div>

<script>
  const checkbox = document.getElementById("checkbox");
  const box = document.getElementById("box");

  const toggle = () => {
    const checked = checkbox.getAttribute("aria-checked") === "true";
    checkbox.setAttribute("aria-checked", String(!checked));
    box.style.background = checked ? "white" : "black";
  };

  checkbox.addEventListener("click", toggle);

  checkbox.addEventListener("keydown", (e) => {
    if (e.key === " " || e.key === "Enter") {
      e.preventDefault();
      toggle();
    }
  });
</script>
  • Atributo aria-disabled: Este estado é usado para indicar que um elemento está desabilitado apenas para pessoas que utilizam tecnologias assistivas, como leitores de tela.
<div
  id="editBtn"
  role="button"
  tabindex="-1"
  aria-disabled="true"
  style="opacity: 0.5; cursor: not-allowed;"
>
  Edit
</div>

<button id="toggle">Toggle Disabled</button>

<script>
  const editBtn = document.getElementById("editBtn");
  const toggleBtn = document.getElementById("toggle");

  toggleBtn.addEventListener("click", () => {
    const disabled = editBtn.getAttribute("aria-disabled") === "true";

    editBtn.setAttribute("aria-disabled", String(!disabled));
    editBtn.tabIndex = disabled ? 0 : -1;
    editBtn.style.opacity = disabled ? "1" : "0.5";
    editBtn.style.cursor = disabled ? "pointer" : "not-allowed";
  });
</script>
  • Atributo aria-selected: Este estado é usado para indicar que um elemento está selecionado. Você pode usar este estado em controles personalizados como uma interface com abas, uma listbox ou uma grade.
<div role="tablist">
  <button role="tab" aria-selected="true">Tab 1</button>
  <button role="tab" aria-selected="false">Tab 2</button>
  <button role="tab" aria-selected="false">Tab 3</button>
</div>

<script>
  const tabs = document.querySelectorAll('[role="tab"]');

  tabs.forEach((tab) => {
    tab.addEventListener("click", () => {
      tabs.forEach(t => t.setAttribute("aria-selected", "false"));
      tab.setAttribute("aria-selected", "true");
    });
  });
</script>
  • Atributo aria-controls: Usado para associar um elemento a outro elemento que ele controla. Isso ajuda pessoas que usam tecnologias assistivas a entender a relação entre os elementos.
<div role="tablist">
  <button 
    role="tab"
    id="tab1"
    aria-controls="section1"
    aria-selected="true"
  >
    Tab 1
  </button>
  <button
    role="tab"
    id="tab2"
    aria-controls="section2"
    aria-selected="false"
  >
    Tab 2
  </button>
  <button
    role="tab"
    id="tab3"
    aria-controls="section3"
    aria-selected="false"
  >
    Tab 3
  </button>
</div>
  • Atributo hidden: Oculta painéis inativos tanto para usuários visuais quanto para usuários de tecnologias assistivas.

Trabalhando com live regions e conteúdo dinâmico

  • Atributo aria-live: Torna parte de uma página da web uma região dinâmica, o que significa que quaisquer atualizações dentro dessa área serão anunciadas por um leitor de tela para que os usuários não percam mudanças importantes.
  • Valor polite: A maioria das regiões dinâmicas usa este valor. Este valor significa que a atualização não é urgente, então o leitor de tela pode esperar até terminar qualquer anúncio atual ou o usuário completar sua ação atual antes de anunciar a atualização.
Aqui está um exemplo de uma região ao vivo que é atualizada dinamicamente por JavaScript:
<div aria-live="polite" id="status"></div>

<button id="updateStatus">Update Status</button>

<script>
  const statusEl = document.getElementById("status");
  const btn = document.getElementById("updateStatus");

  btn.addEventListener("click", () => {
    statusEl.textContent = "Your file has been successfully uploaded.";
  });
</script>
  • Atributo contenteditable: Transforma o elemento em um editor ao vivo, permitindo que os usuários atualizem seu conteúdo como se fosse um campo de texto. Quando não houver um rótulo ou título visível para uma região contenteditable, adicione um nome acessível usando o atributo aria-label para ajudar os usuários de leitores de tela a entender o propósito da área editável.
<div
  contenteditable="true"
  aria-label="Note editor"
  id="editor"
  style="border: 1px solid #ccc; padding: 8px;"
>
  Editable content goes here
</div>

<p id="status" aria-live="polite"></p>

<script>
  const editor = document.getElementById("editor");
  const status = document.getElementById("status");

  editor.addEventListener("input", () => {
    status.textContent = "Content updated";
  });
</script>

Eventos focus e blur

  • Evento blur: Disparado quando um elemento perde o foco.
<input
  id="nameInput"
  type="text"
  placeholder="Type here and click outside"
  aria-label="Name input"
/>

<p id="status" aria-live="polite"></p>

<script>
  const input = document.getElementById("nameInput");
  const status = document.getElementById("status");

  input.addEventListener("blur", () => {
    status.textContent = "Input lost focus";
  });
</script>
  • Evento focus: Disparado quando um elemento recebe foco.
<input
  id="emailInput"
  type="email"
  placeholder="Click or tab into this field"
  aria-label="Email input"
/>

<p id="status" aria-live="polite"></p>

<script>
  const input = document.getElementById("emailInput");
  const status = document.getElementById("status");

  input.addEventListener("focus", () => {
    status.textContent = "Input received focus";
  });
</script>