Addaw, anar a la pàgina d'inici Donar

Lliçó 7 de 10 · 11 min de lectura · Revisada el 26/10/2026

Skip links, gestió del focus en SPA, finestres modals i contingut dinàmic

Estàs llegint una lliçó oberta. Matricula't gratis per fer els tests i els exercicis, desar el teu progrés i aconseguir el certificat.

En una pàgina estàtica el focus es gestiona sol: la persona prem Tab i el navegador fa la resta. Tan bon punt hi entra JavaScript (contingut que apareix, panells que s'obren, vistes que canvien sense recarregar), el desenvolupador passa a ser responsable de decidir on és el focus en cada moment. Si no ho decideix, el focus es queda on era o, pitjor, el navegador el retorna al principi del document. Aquesta lliçó tracta d'aquesta responsabilitat en quatre escenaris: els enllaços de salt, els diàlegs modals, el contingut que apareix i desapareix, i els canvis de vista en aplicacions d'una sola pàgina.

Enllaços de salt (skip links)

Un skip link és un enllaç, normalment el primer element enfocable de la pàgina, que porta directament al contingut principal saltant-se la capçalera i els menús. És la tècnica més senzilla per complir el 2.4.1 Evitar blocs i beneficia qualsevol persona que faci servir teclat, no només qui fa servir lector de pantalla (que, a més, disposa dels landmarks).

Exemple

<body>
  <a href="#contenido" class="skip-link">Salta al contingut principal</a>
  <header>…</header>
  <main id="contenido" tabindex="-1">
    <h1>…</h1>
  </main>
.skip-link {
  position: absolute;
  left: 0; top: 0;
  transform: translateY(-150%);
  background: #1a4fd6; color: #fff; padding: .75rem 1rem;
}
.skip-link:focus { transform: none; }

L'enllaç pot estar amagat fora de la pantalla fins que rep el focus, però llavors s'ha de fer visible: un enllaç de salt que no es veu mai no serveix a qui navega amb teclat sense lector. El tabindex="-1" a <main> garanteix que tots els navegadors mouen realment el focus a la destinació i no només desplacen la vista.

Diàlegs modals

Un diàleg modal és la situació més exigent per al focus. Quan s'obre, han de passar cinc coses:

  1. El focus es mou dins del diàleg: al primer control interactiu, o al mateix contenidor del diàleg si comença amb un text important que cal llegir.
  2. El focus no pot sortir del diàleg amb Tab mentre està obert; quan arriba a l'últim control, torna al primer. Això no és un parany (2.1.2) perquè hi ha una manera estàndard de sortir-ne.
  3. Escapada tanca el diàleg.
  4. El contingut del darrere queda inert: no es pot enfocar ni llegir amb el lector de pantalla.
  5. En tancar-lo, el focus torna a l'element que l'ha obert.

L'element natiu <dialog> amb el mètode showModal() resol els punts 2, 3 i 4 sense codi addicional (el focus no pot arribar a la pàgina del fons, tot i que des de l'últim control pot passar un moment a la interfície del navegador abans de tornar al diàleg), i a més col·loca el diàleg a la capa superior, per sobre de qualsevol z-index. És l'opció recomanada avui; tots els navegadors actuals l'admeten.

<button type="button" id="abrir">Elimina l'expedient</button>

<dialog id="confirmar" aria-labelledby="conf-titulo">
  <h2 id="conf-titulo">Vols eliminar l'expedient 2024/0123?</h2>
  <p>Aquesta acció no es pot desfer.</p>
  <button type="button" id="cancelar">Cancel·la</button>
  <button type="button" id="eliminar">Elimina</button>
</dialog>
const dlg = document.getElementById('confirmar');
const abrir = document.getElementById('abrir');
abrir.addEventListener('click', () => {
  dlg.showModal();
  document.getElementById('cancelar').focus(); // l'opció segura primer
});
dlg.addEventListener('close', () => abrir.focus()); // retornar el focus

Si no pots fer servir <dialog>, l'equivalent ARIA és un contenidor amb role="dialog", aria-modal="true" i aria-labelledby, més JavaScript per al cicle del focus, la tecla Escapada i l'atribut inert a la resta de la pàgina mentre el diàleg és obert. inert és un atribut HTML que fa que un subarbre no sigui enfocable ni perceptible per les tecnologies de suport; funciona a tots els navegadors actuals i substitueix l'antic truc de posar aria-hidden="true" a tots els germans.

Error freqüent

Obrir la finestra modal canviant només una classe CSS. El diàleg apareix visualment, però el focus continua al botó que l'ha obert: la persona prem Tab i recorre el menú de la pàgina del fons sense saber que hi ha un diàleg obert. I en tancar-lo amb la «X», el focus es perd al principi del document. Cada obertura i cada tancament han de moure el focus explícitament.

Infografia · Cicle

El cicle del focus en un diàleg modal

Cinc fases que es repeteixen cada vegada que s'obre un diàleg; dialog amb showModal() les resol gairebé totes

  1. Fase 1: Obrir i moure el focusEl focus entra al primer control o al títol del diàleg
  2. Fase 2: Retenir el focusTab i Maj+Tab circulen només dins del diàleg
  3. Fase 3: Fons inertEl que hi ha darrere no s'enfoca ni es llegeix: inert o showModal()
  4. Fase 4: Tancar amb EscapadaEscapada, Cancel·la o el botó de tancar acaben el diàleg
  5. Fase 5: Retornar el focusEl focus torna al botó que ha obert el diàleg

Després de la fase 5, el cicle torna a començar per la fase 1.

ADDAW · Cursos d'accessibilitat
Llegeix la infografia com a text

Cicle de cinc fases que es repeteix a cada obertura d'un diàleg modal. Fase 1, obrir i moure el focus: el focus entra al primer control o al títol del diàleg. Fase 2, retenir el focus: Tab i Maj+Tab circulen només dins del diàleg, sense que això sigui un parany perquè hi ha una sortida estàndard. Fase 3, fons inert: el que hi ha darrere no es pot enfocar ni llegir, amb l'atribut inert o amb showModal(). Fase 4, tancar: Escapada, el botó Cancel·la o el botó de tancar acaben el diàleg. Fase 5, retornar el focus: torna al botó que ha obert el diàleg, i des d'allà el cicle pot tornar a començar a la propera obertura.

Contingut que apareix i desapareix

No tot el que apareix necessita rebre el focus. La regla és la intenció de la persona:

  • Si la persona ha demanat explícitament obrir alguna cosa amb què interactuarà (un panell de filtres, un formulari inline, un menú), mou el focus al principi d'aquesta cosa.
  • Si el que apareix és informatiu i no requereix interacció (un missatge «Desat», un comptador de resultats), no moguis el focus: anuncia-ho amb una regió activa (role="status").
  • Si el contingut apareix sense que la persona hagi fet res (un avís, un canvi automàtic), no moguis mai el focus, perquè seria un canvi de context no sol·licitat que desorienta (el criteri AAA 3.2.5 Canvis a petició ho exigeix expressament).
  • Si l'element que tenia el focus s'elimina del DOM (per exemple, en esborrar una fila amb el seu botó), mou el focus a un element proper amb sentit: la fila següent, l'encapçalament de la llista o un missatge de confirmació amb tabindex="-1". Si no ho fas, el navegador envia el focus al <body>.

Per als panells que es despleguen al seu lloc (acordions, «mostra'n més»), n'hi ha prou amb aria-expanded al botó: el contingut apareix just després al DOM i la persona hi arriba amb la següent pulsació de Tab o amb les fletxes del lector. No cal moure el focus.

Canvis de vista en aplicacions d'una sola pàgina

En una SPA (React, Vue, Angular, Svelte), prémer un enllaç del menú no recarrega la pàgina: l'encaminador substitueix el contingut de <main>. Per al navegador no ha passat res: el focus continua a l'enllaç del menú, el títol de la pestanya no canvia i el lector de pantalla no anuncia res. La persona que fa servir lector creu que l'enllaç no ha funcionat.

La solució té tres parts, que s'apliquen a cada canvi de ruta:

  1. Actualitzar document.title amb el nom de la vista nova (2.4.2).
  2. Moure el focus al principi de la vista nova: a l'<h1> o al contenidor <main> amb tabindex="-1". Així el lector n'anuncia el títol i la pulsació següent de Tab comença al contingut nou.
  3. Opcionalment, anunciar el canvi en una regió activa («Pàgina carregada: Els meus expedients») per a qui no estigui atent al focus.
// Després de renderitzar la ruta nova
document.title = `${vista.titulo} - Seu electrònica`;
const h1 = document.querySelector('main h1');
h1.setAttribute('tabindex', '-1');
h1.focus();

El curs de CSS, JavaScript i frameworks amplia aquests detalls per a React, Vue i Angular.

Punt clau

Cada vegada que JavaScript canvia el que hi ha a la pantalla, pregunta't: on és ara el focus i on hauria de ser? Si la persona ha demanat el canvi, el focus va al que és nou; si ho tanca, torna on era; si només és informatiu, no es mou i s'anuncia.

Resum

  • Un enllaç de salt visible quan rep el focus, que apunta a <main tabindex="-1">, compleix el 2.4.1 de manera senzilla.
  • Una finestra modal mou el focus a dins, el reté, es tanca amb Escapada, deixa el fons inert i retorna el focus a qui l'ha obert; <dialog> amb showModal() ho resol gairebé tot.
  • El contingut sol·licitat rep el focus; l'informatiu s'anuncia amb una regió activa; el que no s'ha sol·licitat no mou mai el focus.
  • Si elimines l'element enfocat, porta el focus a un veí amb sentit.
  • A cada canvi de ruta d'una SPA: actualitzar document.title, enfocar l'<h1> i, si escau, anunciar-ho.

Portes 4 lliçons d'aquest curs: que no es perdin

Crea el teu compte gratis i desa el que ja has llegit i segueix el curs amb tot:

  • El test d'aquesta lliçó (3 preguntes) per comprovar el que has après
  • Exercicis pràctics amb pàgines reals
  • El teu progrés desat: continues on ho vas deixar, des de qualsevol dispositiu
  • Constància del curs amb les hores i certificat gratuït del nivell 3 quan aprovis l'examen

Entres amb el teu compte de Google o només amb el teu correu electrònic. No cal contrasenya.