理系学生日記

おまえはいつまで学生気分なのか

tabindexで消耗していた話

tabindex には苦しまされることが多いです。

ぼくは以前に tabindex でタブオーダーが明示的に指定されているシステムのテストを行うことになったんですが、タブオーダーが設計上で明示的に指定されている以上タブオーダーがテスト観点に挙がり、打鍵でタブオーダーを一つ一つ確認するというテストを行いましたが、 タブオーダーのようなユーザインタラクションに関するテストについては「どのようにそのテスト実施者が行ったテスト実施結果の正しさを確認するのか」という問題があり、たいへんつらいものでした。

今日のエントリでは、そもそも論として、tabindex の機能、そして、その使い方のベストプラクティスは何なのかを考えてみます。

tabindex とは

tabindex というのは、HTML 仕様で定義されている属性で、

  1. tabindex 属性を付与された要素がフォーカスを持てることを示す
  2. (TAB キー等で)フォーカスを移していく操作によって、フォーカスを移動できるかを示す
  3. フォーカスの移動順(タブオーダー順)を示す

といったことを可能にします。最後に示した フォーカスの移動順 というのは、実は tabindex 属性のオプション機能でしかありません。

tabindex が付与されていないタグに対しては、ブラウザはデフォルトの動作を適用します。 具体的には、<a><audio><button><img><input> といったタグ (これらを Interactive Content と呼びます) に対しては、デフォルトでフォーカスが当たります。 逆に言えば、<span><div> といった Interactive Content ではないタグに対してフォーカスを当てるためには、明示的な tabindex 属性の付与が必要です。つまり、tabindex 属性はタグを Interactive Content に変える、といっても良いかもしれません。

tabindex の使い方

tabindex については、大きく分けて 3 つの使い方があります。

  1. tabindex="0" を指定する
    • 指定されたタグは、ブラウザ上でフォーカスを受けることが可能になります。"0" が指定された場合、そのフォーカス順 (タブオーダー) はブラウザが決めます。ブラウザは通常、DOM 上での出現順にタブオーダーを割り当てます。
  2. tabindex="-1" 等、負の数を指定する
    • 指定されたタグは、フォーカスを当てることは可能になりますが、タブオーダーの中には含まれません(タブを押すだけでは、フォーカスを与えられません)。フォーカスを当てるためには、JavaScript 等で明示する必要があります。
  3. tabindex="1" 等、正の数を指定する
    • 指定されたタグは、ブラウザ上でフォーカスを受けることが可能になります。また、タブオーダー順で考慮されます。

最後の「タブオーダー順で考慮される」っていうのはちょっと複雑なんですが、大まかに言うと、

  1. tabindex が指定されている要素の方が、指定されていない要素よりも前
  2. tabindex の値が正の数の場合、tabindex が 0、または負の数の要素よりも前
  3. あとは大体、tabindex の値順

っていう感じになります。

ベストプラクティス

長々と書いてしまいましたが、tabindex については、

  • 使うなら負の数指定、あるいは 0 指定
  • 正の数は指定するな

が推奨になります。 これは、W3C の HTML 仕様ARIA のベストプラクティス にも記載があります。

W3C HTML 仕様

Using a positive value for tabindex to specify the element’s position in the sequential focus navigation order interacts with the order of all focusable elements. It is error-prone, and therefore not recommended. Authors should generally leave elements to appear in their default order.

ARIA-Practices

When using roving tabindex to manage focus in a composite UI component, the element that is to be included in the tab sequence has tabindex of "0" and all other focusable elements contained in the composite have tabindex of "-1".

正の値を指定しないのは、タブオーダーの指定がミスしやすいからです。

通常のタブの遷移は、いわゆる Z 型、左から右、上から下に遷移するというのが自然なタブオーダーとされていて、普通に HTML/CSS を書くと、このあたりはブラウザが標準で行ってくれます。 しかし、tabindex 属性で正の値を指定する場合、これらのタブオーダー遷移が崩れやすくなります。要素の追加、削除に伴なって、tabindex の値を正しく保つのは並大抵のことではありません。

この保守コストを支払ってでも、タブオーダーを操作したいケースというのはそうそうあるものではないと思うので、tabindex を使って明示的にタブオーダーを操作する必要があるのかは、慎重に考えた方が良いところだと思います。