Tester les Éléments Personnalisés Utilisant ElementInternals

This page is not available in the language you requested. You have been redirected to the English version of the page.
Link to this page copied to clipboard

Comment Axe DevTools pour le Web lit les sémantiques ARIA à partir d'ElementInternals, ce que vos composants doivent faire pour les exposer, et quelles langues le supportent.

Not for use with personal data

Les composants web peuvent déclarer leurs sémantiques d'accessibilité en JavaScript au lieu de le faire dans le balisage, en appelant attachInternals() et en définissant des propriétés telles que role et ariaLabel sur l'objet ElementInternals retourné. Un composant construit de cette manière n'a pas d'attributs role ou aria-* dans le DOM, de sorte qu'un outil de test qui lit uniquement les attributs voit un élément sans sémantique du tout.

Axe DevTools pour le Web lit maintenant ces sémantiques. Cette page explique quelles versions le supportent, ce que vos composants doivent faire pour rendre leurs internals visibles, et comment les internals interagissent avec les attributs déjà présents sur l'élément. Pour des informations générales sur le choix de votre version axe-core, voir Personnalisation des Règles.

Langues et Versions Supportées

Le support provient du moteur de test d'accessibilité sous-jacent, donc il est disponible partout où la version axe-core intégrée est 4.13.0 ou supérieure.

Langue Version qui a ajouté le support Axe-core intégré
C# 4.13.0 4.13.0
Python 4.13.0 4.13.0
Ruby 4.13.0 4.13.0 (via axe-core-api 4.13.0)
Java 4.13.0 4.13.0 (via com.deque.html.axe-core 4.13.0)
Node.js et le CLI 4.13.0 4.13.0
note

Il n'y a rien à activer. Dans chaque langue ci-dessus, le moteur de test d'accessibilité fonctionne directement sur la page à tester, donc il lit ElementInternals à chaque analyse sans option, drapeau ou changement de configuration. Si vos composants suivent déjà le protocole décrit ci-dessous, il n'y a qu'à mettre à jour.

Exposer ElementInternals à Axe DevTools

ElementInternals est privé par conception : JavaScript ne fournit aucune API pour lire les internals d'un autre élément. Un outil de test ne peut voir l'objet que si votre composant le publie délibérément. Les auteurs de composants le font par le biais d'un protocole communautaire, et Axe DevTools supporte toutes les formes de ce protocole.

La forme recommandée est un WeakMap global, qui garde l'objet en dehors de l'élément lui-même :

customElements.define(
  'my-custom-button',
  class MyCustomButton extends HTMLElement {
    constructor() {
      super();

      const internals = this.attachInternals();
      internals.role = 'button';

      globalThis._elementInternals ??= new WeakMap();
      globalThis._elementInternals.set(this, internals);
    }
  }
);

Attribuer l'objet à une propriété sur l'élément fonctionne aussi :

customElements.define(
  'my-custom-button',
  class MyCustomButton extends HTMLElement {
    constructor() {
      super();

      this._internals = this.attachInternals();
      this._internals.role = 'button';
    }
  }
);

En plus de la carte globalThis._elementInternals, Axe DevTools recherche l'objet sous n'importe lequel de ces noms sur l'élément :

  • _internals (recommandé)
  • internals
  • internals_
  • Symbol('internals')
  • Symbol('privateInternals')
important

Un composant qui garde ses internals réellement privés est invisible pour Axe DevTools, et c'est la raison la plus courante de ne voir aucun changement après la mise à jour. Deux cas à vérifier :

  • Champs de classe privés. this.#internals = this.attachInternals() ne peuvent pas être lus de l'extérieur de la classe, donc aucun outil de test ne peut le voir. Plusieurs bibliothèques de composants utilisent ce modèle.
  • Accesseurs. Une propriété définie comme un accessoire plutôt qu'une valeur simple est ignorée, même si elle est nommée _internals. Attribuez l'objet directement.

Dans les deux cas, la correction appartient au composant, non à votre configuration de test. Publiez l'objet par l'une des formes ci-dessus, ou attendez-vous à ce que l'élément soit évalué comme s'il n'avait pas de sémantiques ARIA.

Tester que vos composants sont visibles pour Axe DevTools n'a pas besoin d'un scan. Dans la console du navigateur sur une page qui utilise le composant, globalThis._elementInternals?.get(document.querySelector('my-custom-button')) ou document.querySelector('my-custom-button')._internals devrait renvoyer un objet ElementInternals plutôt que undefined.

Ce que lit Axe DevTools

Une fois que l'objet est visible, Axe DevTools lit la propriété role et les propriétés ARIA qui correspondent aux attributs aria-*, tels que ariaLabel pour aria-label, ariaDescription pour aria-description, et ariaLabelledByElements pour aria-labelledby. Ces valeurs alimentent les mêmes règles qui auraient été appliquées aux attributs équivalents, de sorte qu'un élément personnalisé qui fixe role et ariaLabel via les internals est vérifié pour un nom accessible de la même manière qu'un élément qui fixe role et aria-label dans le balisage.

Prépondérance

Les valeurs présentes dans le DOM l'emportent toujours. Cela signifie que l'ajout d'attributs à votre balisage est un moyen fiable de remplacer ce qu'un composant déclare en interne, et que les internals ne masquent jamais un problème visible dans le DOM.

  • Rôle. Le role de ElementInternals est considéré comme le rôle implicite de l'élément, ayant le même statut que le rôle intégré d'un élément natif. Un attribut role explicite sur l'élément le remplace.
  • Valeurs ARIA. Chaque valeur est d'abord résolue à partir de l'attribut, puis de la propriété correspondante sur l'élément, et seulement ensuite à partir de ElementInternals.

Limitations

Le support est réel mais pas encore complet, et il est utile de connaître les lacunes avant d'interpréter une analyse.

Les règles qui se basent sur les attributs ne correspondent pas aux éléments internes uniquement. Certaines règles identifient les éléments auxquels elles s'appliquent avec un sélecteur CSS sur le DOM. Comme un élément dont la sémantique provient uniquement de ElementInternals n'a pas d'attribut role, ces règles ne sont jamais appliquées à cet élément. Par exemple, aria-required-attr sélectionne [role], et aria-command-name sélectionne [role="link"], [role="button"], [role="menuitem"]. Un élément personnalisé déclarant role = 'button' via des internes n'est évalué par aucune des deux règles. Les règles qui résolvent le rôle plutôt que de correspondre à un sélecteur, telles que celles calculant un nom accessible, s'appliquent.

Les valeurs de rôle ne sont pas validées. Un role défini via des internes est utilisé tel quel. Une valeur invalide ou mal orthographiée n'est pas signalée de la même manière qu'un attribut role invalide le serait.

Certaines règles ne sont appliquées que partiellement. Les règles qui examinent la relation d'un élément avec ses enfants ou ancêtres, telles que aria-required-children, peuvent ne pas évaluer complètement un élément uniquement interne.

En raison de la première limitation en particulier, attendez-vous à ce qu'un composant qui déclare sa sémantique uniquement par des internes produise moins de résultats que le balisage équivalent ne le ferait, plutôt que plus. Une analyse propre d'un tel composant constitue une preuve moins solide qu'une analyse propre d'un composant utilisant des attributs.