# L’Interactivity API et les lecteurs d’écran lors d’une mise à jour du DOM

> Un filtre change à l'écran sans recharger la page, mais un lecteur d'écran ne le sait pas toujours. Comprendre pourquoi et régler ça avec les régions live.

- Auteur : WordPress Développement
- Publié le : 2024-09-03
- Mis à jour le : 2024-09-03
- Catégorie : Accessibilité
- URL : https://www.wpmoderne.fr/accessibilite/interactivity-api-lecteurs-ecran-mise-a-jour-dom/

## L’essentiel

- Une mise à jour visuelle n'est pas une annonce sonore
- aria-live doit exister avant le changement, pas après
- data-wp-watch ne suffit pas sans rôle ARIA adapté

« Le DOM peut désormais être mis à jour de façon déclarative, sans écrire de JavaScript impératif. » C'est ainsi que la documentation officielle de l'Interactivity API décrit sa promesse principale depuis sa stabilisation dans WordPress 6.5. Cette promesse tient techniquement : un clic sur un filtre de produits, un changement d'onglet ou l'ouverture d'un panneau peuvent désormais modifier le contenu affiché sans rechargement, avec quelques attributs `data-wp-*` posés directement dans le balisage.

Mais cette fluidité visuelle cache un angle mort. Un changement de DOM piloté par `data-wp-bind` ou `data-wp-text` reste, par défaut, silencieux pour un utilisateur de lecteur d'écran. Le texte change à l'écran, la structure ARIA reste identique, et rien n'est annoncé. Ce billet détaille ce qui se passe réellement sous le capot et comment éviter ce piège sur un bloc interactif construit avec cette API.

## Ce que fait réellement l'Interactivity API

L'Interactivity API repose sur un store JavaScript déclaré via `store()`, associé à un ou plusieurs éléments du DOM par l'attribut `data-wp-interactive`. Chaque élément peut ensuite lire ou modifier un contexte partagé avec `data-wp-context`, réagir à des événements avec `data-wp-on--click`, ou refléter une valeur du store avec `data-wp-text`, `data-wp-bind--*` ou `data-wp-class--*`.

Le mécanisme interne s'appuie sur Preact et un algorithme de diffing : seules les portions du DOM réellement modifiées sont retouchées, ce qui explique la fluidité perçue. Techniquement, aucune requête réseau, aucun rechargement de page, aucune perte de focus liée à un remplacement complet du DOM. C'est un vrai progrès par rapport à un ancien bloc qui rechargeait toute une zone via `innerHTML`.

## Pourquoi le lecteur d'écran ne suit pas la mise à jour

> L'essentiel à retenir : Une mise à jour visuelle n'est pas une annonce sonore ; aria-live doit exister avant le changement, pas après ; data-wp-watch ne suffit pas sans rôle ARIA adapté

Un lecteur d'écran ne surveille pas en continu tout le DOM d'une page : il s'appuie sur l'arbre d'accessibilité (accessibility tree) et sur des mécanismes précis pour savoir quand annoncer un changement. Le principal mécanisme pour un contenu qui change dynamiquement est la région live ARIA, posée via l'attribut `aria-live="polite"` ou `aria-live="assertive"`, ou via les rôles `role="status"` et `role="alert"` qui embarquent implicitement ce comportement.

Le piège le plus fréquent observé sur des blocs migrés vers l'Interactivity API : l'attribut `aria-live` est ajouté dynamiquement en même temps que le contenu, via `data-wp-bind--aria-live`, au lieu d'être présent dès le rendu initial. Or la plupart des lecteurs d'écran n'enregistrent une région comme « live » qu'au moment où elle apparaît déjà marquée ainsi dans le DOM. Si l'attribut arrive en même temps que le texte qu'il est censé annoncer, l'annonce est perdue.

## Corriger un compteur de résultats piloté par l'Interactivity API

Prenons un bloc de filtre de produits construit avec cette API. Le balisage initial doit déjà contenir la région live, vide ou avec un texte par défaut, et seul son contenu change ensuite :

```
<div data-wp-interactive="monboutique/filtres">
  <div
    class="resultats-annonce"
    aria-live="polite"
    role="status"
    data-wp-text="state.libelleResultats"
  >12 produits affichés</div>
</div>
```

Côté store, la fonction qui traite un clic sur un filtre met à jour `state.libelleResultats` avec une phrase complète plutôt qu'un simple nombre isolé, plus facile à interpréter hors contexte visuel :

```
import { store, getContext } from '@wordpress/interactivity';

store('monboutique/filtres', {
  actions: {
    appliquerFiltre() {
      const context = getContext();
      const resultats = calculerResultats(context.filtreActif);
      const state = { libelleResultats: `${resultats.length} produits affichés` };
      Object.assign(store('monboutique/filtres').state, state);
    },
  },
});
```

Point important : la région live ne doit pas être détruite puis recréée à chaque rendu. Si le composant remonte entièrement l'élément parent, la région perd son statut de région live enregistrée et l'annonce ne se produit plus. C'est pour cela qu'il faut garder l'élément porteur de `aria-live` stable dans l'arborescence, et ne faire varier que son contenu texte.

### Les cas où le silence est voulu

Toute mise à jour de DOM ne mérite pas une annonce. Un changement purement décoratif, une animation de survol, un déplacement visuel sans conséquence fonctionnelle n'ont rien à annoncer. Multiplier les régions live sur une page finit par produire l'effet inverse de celui recherché : un flux vocal permanent, désagréable, que beaucoup d'utilisateurs expérimentés finissent par couper entièrement dans leur lecteur d'écran.

- Réserver `aria-live="assertive"` aux messages qui interrompent réellement une tâche (erreur bloquante, changement critique).
- Préférer `aria-live="polite"` pour les mises à jour d'information secondaire (nombre de résultats, statut d'un panier).
- Ne jamais empiler deux régions live concurrentes sur le même changement de state.
- Tester avec NVDA ou VoiceOver après chaque ajout de directive `data-wp-bind--aria-live`.

## Vérifier ce comportement sans attendre un audit complet

Un test rapide et fiable consiste à ouvrir NVDA, à activer un filtre, puis à observer si une annonce vocale suit le changement affiché. Si rien ne se produit, l'inspecteur d'accessibilité du navigateur permet de vérifier deux choses : l'attribut `aria-live` est-il déjà présent avant l'interaction, et l'élément qui le porte reste-t-il le même nœud DOM après le rendu du store.

> Sur nos projets, poser la région live dans le balisage statique du bloc, avant toute hydratation par l'Interactivity API, évite neuf pièges sur dix liés aux annonces manquantes.

## En résumé

L'Interactivity API change la façon d'écrire de l'interactivité dans WordPress, mais elle ne change rien aux règles d'accessibilité qui existaient déjà pour le contenu dynamique. Une région live doit être présente dès le premier rendu, rester stable dans l'arborescence, et ne porter que les changements qui ont un sens fonctionnel pour l'utilisateur. Sans ces trois conditions, un bloc parfaitement fluide visuellement reste muet pour un lecteur d'écran.
