マウスを使わずキーボードだけでWebページを操作するとき、Tabキーを押すとフォーカスがリンクやボタンへ次々と移っていきます。この移動する順番を制御するのがtabindex属性です。この記事では、tabindexの3種類の値の意味と使い分け、そして正の値を避けるべき理由までを、実際に動くコードとともに丁寧に解説します。
目次
tabindex とは何か/なぜ必要か
tabindexは、要素がキーボードのフォーカスを受け取れるかどうか、また受け取る場合の順序を指定するためのグローバル属性です。ほとんどのHTML要素に付けることができます。
Webページはマウスだけで操作されるとは限りません。キーボードのみを使う人、スクリーンリーダーを使う人など、さまざまな利用者がいます。そうした環境では、Tabキーで操作可能な要素へ順番にフォーカスを移し、Enterキーやスペースキーで操作するのが基本です。このときフォーカスが当たる要素や順序が適切でないと、ページが使いづらくなったり、まったく操作できなくなったりします。
そもそも<a href="...">や<button>、<input>といったインタラクティブな要素は、tabindexを書かなくても最初からキーボードでフォーカスできます。フォーカスの順序も、原則としてHTMLに書かれた順(DOMの順序)に従います。tabindexは、この標準の挙動を変えたいときや、本来フォーカスできない要素をフォーカス可能にしたいときに使う属性だと考えると分かりやすいでしょう。
3種類の値の意味
tabindexに指定する値は、大きく分けて「負の値」「0」「正の値」の3種類で挙動が変わります。それぞれの意味を次のテーブルで整理します。
| 値 | 意味 |
|---|---|
tabindex="0" | 要素をキーボードでフォーカス可能にします。フォーカス順序はDOMの順序に従い、通常のフォーカス順に組み込まれます。 |
tabindex="-1" | 要素をフォーカス可能にしますが、Tabキーによる移動順序には含めません。JavaScriptのfocus()やクリックでのみフォーカスできます。 |
tabindex="1"以上(正の値) | DOMの順序を無視して、値の小さいものから優先的にフォーカスが当たるよう、順序を明示的に指定します。基本的に使用は推奨されません。 |
正の値は複数の要素に指定した場合、値が小さい要素(1→2→…)の順にフォーカスされ、そのあとでtabindex="0"の要素や標準でフォーカス可能な要素がDOM順にフォーカスされます。つまり正の値を持つ要素が常に先に来る点に注意が必要です。
基本の使い方
tabindex=”0″ でフォーカス可能にする
<div>や<span>のような要素は、本来キーボードでフォーカスできません。これらをtabindex="0"にすると、Tabキーでフォーカスが当たるようになり、フォーカス順序はDOMの順序どおりになります。
<a href="#">リンク1</a>
<!-- 本来フォーカスできない div をフォーカス可能にする -->
<div tabindex="0">この div も Tab キーでフォーカスできます</div>
<a href="#">リンク2</a>
この例では、Tabキーを押すたびに「リンク1」→「div」→「リンク2」の順にフォーカスが移動します。tabindex="0"はあくまでDOMの順序に沿って組み込まれるため、書いた場所どおりの自然な順番になります。
tabindex=”-1″ でプログラム的にフォーカスを当てる
tabindex="-1"を付けた要素は、Tabキーの移動順序には含まれませんが、JavaScriptのelement.focus()を呼ぶとフォーカスを当てられます。エラーメッセージの表示位置やダイアログの見出しなど、「特定の操作をきっかけにフォーカスを移したいが、通常のTab移動には入れたくない」場面で役立ちます。
<button id="showBtn">メッセージを表示</button>
<!-- Tab では飛ばされるが focus() で移動できる -->
<p id="message" tabindex="-1">処理が完了しました。</p>
<script>
const btn = document.getElementById("showBtn");
const message = document.getElementById("message");
btn.addEventListener("click", () => {
// クリックをきっかけにメッセージへフォーカスを移す
message.focus();
});
</script>
ボタンを押すと、tabindex="-1"を付けた<p>要素にフォーカスが移動します。スクリーンリーダーはフォーカスが移った要素の内容を読み上げるため、動的に表示したメッセージを利用者に確実に伝えたい場面で効果的です。tabindex="-1"がなければfocus()は効かないので、この値が必要になります。
正の値(1以上)を使わない方がよい理由
tabindexに1以上の正の値を指定すると、フォーカス順序を自由に決められます。一見便利そうですが、実際のプロジェクトではほぼ使うべきではありません。基本は0と-1だけで十分だと覚えておいてください。
DOMの順序と食い違い、混乱を生む
正の値を持つ要素は、DOMのどこに書かれていても、標準でフォーカス可能な要素より先にフォーカスされます。次の例を見てください。
<input type="text" placeholder="名前">
<input type="text" placeholder="メール" tabindex="1">
<button>送信</button>
この場合、Tabキーではまずtabindex="1"の「メール」欄にフォーカスが当たり、そのあとで「名前」欄、「送信」ボタンへと移動します。見た目の並び順は「名前」→「メール」→「送信」なのに、フォーカス順序が「メール」→「名前」→「送信」となり、利用者を混乱させてしまいます。
保守が破綻しやすい
正の値でフォーカス順を管理し始めると、要素を1つ追加・削除するたびに全体の番号を振り直す必要が出てきます。ページが大きくなるほど番号の管理は複雑になり、抜けや重複が生じてフォーカス順序が壊れやすくなります。
そもそもフォーカス順序を変えたくなったら、正の値で無理に上書きするのではなく、HTMLの要素の並び順そのものを見直すのが正攻法です。DOMの順序と見た目の順序、フォーカスの順序が一致していれば、余計なtabindexは不要になります。こうしておくとメンテナンスも格段に楽になります。
フォーカスできても操作できないことがある
tabindex="0"を付けると要素にフォーカスは当たりますが、それだけでボタンのように「押せる」ようになるわけではありません。ここは特につまずきやすいポイントです。
ボタン相当にするなら role とキーボードイベントも必要
たとえば<div>をボタンとして使いたい場合、tabindex="0"でフォーカス可能にするだけでは不十分です。本物の<button>はEnterキーやスペースキーで発火しますが、<div>にはその挙動がないため、自分でキーボードイベントを実装する必要があります。あわせてrole="button"を付けて、スクリーンリーダーに「これはボタンだ」と伝えることも欠かせません。
<div role="button" tabindex="0" id="myBtn">送信する</div>
<script>
const myBtn = document.getElementById("myBtn");
function doSubmit() {
console.log("送信しました");
}
myBtn.addEventListener("click", doSubmit);
// Enter キーとスペースキーでも発火させる
myBtn.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
doSubmit();
}
});
</script>
このように、tabindexだけでなくroleやキーボードイベントを組み合わせて、ようやく本物のボタンに近い操作性が得られます。逆に言えば、最初から<button>要素を使えば、これらはすべて自動で備わっています。フォーカスやキーボード操作が必要なときは、まず適切なHTML要素で実現できないかを検討するのがおすすめです。
正の値でフォーカス順が混乱したときの対処
既存のコードでフォーカス順が不自然だと感じたら、まずtabindex="1"以上の正の値が残っていないかを疑ってください。正の値が混ざっていると、その要素だけ順序が飛んでしまい、原因が分かりにくくなります。見つけたら、正の値を0に置き換えたうえで、HTMLの並び順を意図した順序に整えるのが確実な解決策です。
まとめ
tabindex属性は、キーボードのフォーカス順序や、要素をフォーカス可能にするかどうかを制御するための属性です。実務で使う値は基本的に2つで、tabindex="0"は本来フォーカスできない要素を通常のフォーカス順に組み込むとき、tabindex="-1"はTab移動には含めずJavaScriptのfocus()でだけフォーカスを当てたいときに使います。
1以上の正の値はDOM順と食い違い、保守も破綻しやすいため避けましょう。フォーカス順を変えたいときは、正の値ではなくHTMLの並び順そのものを見直すのが正解です。また、フォーカスできることと操作できることは別問題なので、<div>をボタン化するならroleやキーボードイベントもあわせて実装しましょう。できる限り<button>などの適切な要素を使えば、こうした手間はほとんど不要になります。