セマンティック HTML 要素の DOM 位置で決まる既定のタブオーダーは便利ですが、タブオーダーの変更が必要になる場合もあります。HTML 内の要素を移動するのが理想的ですが、現実的でない場合があります。このような場合は、tabindex
HTML 属性を使用して、要素のタブ位置を明示的に設定できます。
tabindex
は任意の要素に適用できますが、必ずしもすべての要素で有用であるとは限りません。また、広範な整数値を使用します。tabindex
を使用すると、フォーカス可能なページ要素の明示的な順序を指定したり、フォーカス不可能な要素をタブオーダーに追加したり、要素をタブオーダーから削除したりできます。例:
tabindex="0"
: 要素を通常のタブオーダーに追加します。要素は Tab キーを押すとフォーカスされ、focus()
メソッドを呼び出すことでフォーカスがあたります。
tabindex="-1"
: 通常のタブオーダーから要素を削除しても、focus()
メソッドを呼び出すことでまだ要素にフォーカスできます。
tabindex="5"
: 0
より大きい tabindex を指定すると、その要素が自然なタブオーダーの前に表示されます。複数の要素の tabindex が 0
より大きい場合、タブオーダーは 0 より大きい最小値からスタートし、次第に大きい値の要素に移動します。
これは、特にヘッダー、画像、記事のタイトルなどの非入力要素に当てはまります。可能な場合は、DOM シーケンスが論理的なタブオーダーを提供するようにソースコードを配置することをおすすめします。tabindex
を使用する対象は、ボタン、タブ、プルダウン、テキスト フィールドなどのカスタム インタラクティブ コントロール、つまり、ユーザーが入力を想定する要素に制限してください。
tabindex
はインタラクティブなコンテンツにのみ追加します。重要なコンテンツ(キー画像など)であっても、スクリーン リーダーのユーザーはフォーカスを追加しなくてもコンテンツを理解できます。
ページレベルでフォーカスを管理する
シームレスなユーザー エクスペリエンスを実現するために tabindex
が必要な場合があります。たとえば、さまざまなコンテンツのセクションを含む堅牢なシングルページを構築しているとします。すべてのコンテンツを一度に見ることはできません。つまり、ナビゲーション リンクをクリックすると、ページを更新せずに表示可能なコンテンツが変更される可能性があります。
この場合、選択したコンテンツ領域を特定し、tabindex
に -1
を指定して、その focus
メソッドを呼び出します。これにより、コンテンツが通常のタブオーダーに表示されなくなります。この手法はフォーカスの管理と呼ばれ、ユーザーが知覚する状況を、サイトの視覚的なコンテンツに同期させることができます。
コンポーネントのフォーカスの管理
場合によっては、カスタム コンポーネントなど、コントロール レベルでもフォーカスを管理する必要があります。
たとえば、select
要素は基本フォーカスを受け取ることができますが、一度フォーカスされると、矢印キーを使用して追加の選択可能なオプションを表示できます。カスタムの select
要素を作成する場合は、キーボード ユーザーがコントロールを操作できるように、その動作を再現することが重要です。
実装するキーボードの動作を把握するのが難しい場合があります。Accessible Rich Internet Applications (ARIA) Authoring Practices ガイドには、タイプ別コンポーネントの一覧と、サポートしているキーボードの操作が記載されています。
一連のラジオボタンに類似した、独自の外観と動作を持つカスタム要素を作成しているとします。
<radio-group>
<radio-button>Water</radio-button>
<radio-button>Coffee</radio-button>
<radio-button>Tea</radio-button>
<radio-button>Cola</radio-button>
<radio-button>Ginger Ale</radio-button>
</radio-group>
必要なキーボード サポートを判断するには、ARIA 作成ガイドをご覧ください。セクション 2 には、無線グループの特性表(新しい要素に最も近い既存のコンポーネント)などの設計パターンの一覧が記載されています。
サポートすべき一般的なキーボード動作の一つに、上/下/左/右矢印キーがあります。この動作を新しいコンポーネントに追加するには、tabindex の移動と呼ばれる手法を使用します。
tabindex の移動を機能させるには、現在アクティブな子を除いて、すべての子の tabindex
を -1 に設定します。
<radio-group>
<radio-button tabindex="0">Water</radio-button>
<radio-button tabindex="-1">Coffee</radio-button>
<radio-button tabindex="-1">Tea</radio-button>
<radio-button tabindex="-1">Cola</radio-button>
<radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>
コンポーネントは、キーボードのイベント リスナーを使用して、ユーザーが押したキーを判断します。以前にフォーカスを設定した子の tabindex
は -1 に設定し、次にフォーカスされる子の tabindex
を 0 にして、その子の focus メソッドを呼び出します。
<radio-group>
<!-- Assuming the user pressed the down arrow, we'll focus the next available child -->
<radio-button tabindex="-1">Water</radio-button>
<radio-button tabindex="0">Coffee</radio-button> // call .focus() on this element
<radio-button tabindex="-1">Tea</radio-button>
<radio-button tabindex="-1">Cola</radio-button>
<radio-button tabindex="-1">Ginger Ale</radio-button>
</radio-group>
ユーザーが最後(またはフォーカスの移動方向によっては先頭)の子まで到達すると、フォーカスは先頭(または最後)の子に戻ります。
次の例を試してみてください。DevTools で要素を調べると、tabindex が次のラジオボタンに移動する様子がわかります。
モーダルとキーボード トラップ
フォーカスを手動で管理すると複雑な状況になる可能性があるため、手動で管理しないことをおすすめします。たとえば、フォーカスを管理し、タブの動作をキャプチャしようとするオートコンプリート ウィジェットでは、完了するまでユーザーがウィジェットを離れないようにします。これはキーボード トラップと呼ばれ、ユーザーに大きなストレスを与える可能性があります。
WCAG のセクション 2.1.2 では、キーボードのフォーカスが特定のページ要素でロックまたはトラップされないようにすることが規定されています。ユーザーはキーボードのみを使用して、すべてのページ要素間を移動できる必要があります。
このルールの例外はモーダルです。ただし、モーダルを作成する際は、tabindex
を使用しないでください。inert
を使用すると、ユーザーが要素を誤って操作できないようにすることができます(意図的なキーボード トラップ)。デフォルトで無効になっている <dialog>
要素を使用して、ユーザー向けのモーダルを作成し、モーダルの外側のクリックとタブをブロックします。これにより、ユーザーは必要な選択に集中できます。