ElementInternalsを利用するカスタム要素のテスト
如何 にしてAxe DevTools for WebがElementInternalsからARIAセマンティクスを読み取り、コンポーネントがそれらを公開するために必要なこと、そしてどの言語がそれをサポートしているか。
Webコンポーネントは、attachInternals()を呼び出し、roleやariaLabelのようなプロパティを返されたElementInternalsオブジェクトに設定することで、JavaScript内でアクセシビリティセマンティクスを宣言できます。この方法で構築されたコンポーネントには、DOMにroleやaria-*属性がないため、属性のみを読み取るテストツールは意味のない要素として認識します。
Axe DevTools for Webはこれらのセマンティクスを読み取ります。このページでは、どのバージョンがそれをサポートしているか、コンポーネントがその内部を見えるようにするために何をする必要があるか、そして内部がすでに要素上の属性とどう相互作用するかを説明します。axe-coreバージョンの選択に関する一般的な情報は、ルールのカスタマイズを参照してください。
サポートされる言語とバージョン
サポートは基盤となるアクセシビリティテストエンジンから提供されるため、バンドルされているaxe-coreのバージョンが4.13.0以上であれば利用可能です。
| 言語 | サポートが追加されたバージョン | バンドルされたaxe-core |
|---|---|---|
| C# | 4.13.0 | 4.13.0 |
| Python | 4.13.0 | 4.13.0 |
| Ruby | 4.13.0 | 4.13.0 (axe-core-api 4.13.0 通じて) |
| Java | 4.13.0 | 4.13.0 (com.deque.html.axe-core 4.13.0 通じて) |
| Node.jsとCLI | 4.13.0 | 4.13.0 |
何も設定する必要はありません。上記のすべての言語で、アクセシビリティテストエンジンはテストされるページ内で直接実行されるため、オプション、フラグ、または設定変更なしに毎回のスキャンでElementInternalsを読み取ります。コンポーネントが下記で説明されているプロトコルにすでに従っている場合、アップグレードするだけで十分です。
ElementInternalsをAxe DevToolsに公開する
ElementInternalsは設計上非公開です: JavaScriptは他の要素の内部を読み取るAPIを提供しません。テストツールは、コンポーネントが意図的に公開した場合にのみオブジェクトを見ることができます。コンポーネントの作者はこれをコミュニティープロトコルを通じて行い、Axe DevToolsはそのプロトコルのすべての形式をサポートします。
推奨形式は、オブジェクトを要素自体から離しておくためのグローバルWeakMapです:
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);
}
}
);オブジェクトを要素のプロパティに割り当てることも機能します:
customElements.define(
'my-custom-button',
class MyCustomButton extends HTMLElement {
constructor() {
super();
this._internals = this.attachInternals();
this._internals.role = 'button';
}
}
);globalThis._elementInternalsマップの他に、Axe DevToolsは要素上のこれらの名前の下にオブジェクトを探します:
_internals(推奨)internalsinternals_Symbol('internals')Symbol('privateInternals')
内部を本当にプライベートに保つコンポーネントは、Axe DevToolsには見えません。これは、アップグレード後に変化が見られない最も一般的な理由です。確認すべき2つのケース:
- プライベートクラスフィールド。
this.#internals = this.attachInternals()はクラスの外からは読めないため、テストツールは見ることができません。いくつかのコンポーネントライブラリはこのパターンを使用しています。 - ゲッター。 アクセサとして定義されたプロパティは通常の値としてはスキップされ、たとえそれが
_internalsという名前であっても避けられます。オブジェクトを直接割り当ててください。
どちらの場合も、修正はテスト構成ではなくコンポーネントに属します。上記の形式のいずれかを通じてオブジェクトを公開するか、または要素がARIAセマンティクスを持たないかのように評価されることを期待します。
コンポーネントがAxe DevToolsに対して見えるかどうかのテストにはスキャンは不要です。コンポーネントを使用しているページのブラウザコンソールで、globalThis._elementInternals?.get(document.querySelector('my-custom-button'))またはdocument.querySelector('my-custom-button')._internalsがElementInternalsオブジェクトを返すか、undefinedを返すかを確認してください。
Axe DevToolsが読み取る内容
オブジェクトが見えるようになると、Axe DevToolsはroleプロパティと、aria-*属性に対応するARIAプロパティを読み取ります。例えば、ariaLabelはaria-label、ariaDescriptionはaria-description、ariaLabelledByElementsはaria-labelledbyに対応します。これらの値は、同等の属性に対して実行されるものと同じルールを送り出し、内部を通じてroleとariaLabelを設定するカスタム要素は、マーキングでroleとaria-labelを設定するものと同じ方法でアクセス可能な名前が求められます。
優先順位
DOMに存在する値が常に優先されます。これは、マークアップに属性を追加することで、コンポーネントが内部で宣言したものを確実に上書きでき、内部がDOMに見える問題を隠すことがないことを意味します。
- 役割。
roleからのElementInternalsは、要素の暗黙的な役割として扱われ、ネイティブ要素の組み込み役割と同じ扱いとなります。要素上の明示的なrole属性がそれを上書きします。 - ARIA値。 各値は最初に属性から解決され、次に要素上の対応するプロパティから解決され、そして
ElementInternalsから解決されます。
制限事項
サポートが現実にあるが、まだ完全ではなく、スキャンを解釈する前に知っておく価値があるギャップが存在します。
属性に基づくルールは、内部のみの要素にはマッチしません。 多くのルールは、DOMに対するCSSセレクタを使用して適用先の要素を特定します。ElementInternalsからのみセマンティクスを持つ要素にはrole属性がないため、これらのルールはそれに対しては実行されません。たとえば、aria-required-attrは[role]を選択し、aria-command-nameは[role="link"], [role="button"], [role="menuitem"]を選択します。内部を通じてrole = 'button'を宣言するカスタム要素は、いずれのルールによっても評価されません。アクセシブルな名前を計算するような、セレクタとマッチするのではなく役割を解決するルールは適用されます。
役割の値は検証されません。 内部を通じて設定されたroleは、与えられたまま使用されます。無効または誤ったスペルの値は、無効なrole属性が報告されるようには報告されません。
いくつかのルールは部分的にのみ適用されます。 aria-required-childrenのように、要素の子や祖先との関係を検査するルールは、内部のみの要素を完全には評価しない場合があります。
特に最初の制限のため、セマンティクスを内部のみで宣言するコンポーネントは、同等のマークアップである場合よりも検出が少なくなることを期待してください。そのようなコンポーネントのクリーンスキャンは、属性を使用した場合のクリーンスキャンよりも弱い証拠となります。
関連ページ
- ルールのカスタマイズ があなたのスキャンで使用するaxe-coreバージョンを選択します。
- Axe DevTools for Web APIsについて 対応する言語とフレームワークの完全なリストです。
- C# APIドキュメント、Python概要、Rubyを使った高度なAPI使用法、Java API概要、Node.jsおよびブラウザベースのテスト がこれをサポートする言語バインディングです。
