ウェブサイトの「タブ」に求められる機能
タブあるじゃないですか、タブ。こういうの。
流行りなんですかね。しばらく前からサイトの規模に関わらず、新規構築やフルリニューアルでは必ずといって良いほど導入されるようになってきました。
タブといっても大きく2種類あって、タブを選択したときの飛び先が別ページなもの(機能としては普通のリンクと同じ)と、 JavaScript で切り替えるタイプですが、今回話をするのは後者の方です。
マークアップ
まずは JavaScript のことは考えずにマークアップをしてみます。
スクリプトが無効な場合、各タブ部分をa要素によるアンカーにしておき、当該タブパネルへジャンプできるようにしておくのが良いと思います。
また、 WAI-ARIA を使って role や aria-* 属性を設定するとより良いでしょう。
<div class="tab_module">
<ul role="tablist">
<li role="tab" aria-controls="tabpanel-1" id="tab-1"><a href="#tabpanel-1">タブ1</a></li>
<li role="tab" aria-controls="tabpanel-2" id="tab-2"><a href="#tabpanel-2">タブ2</a></li>
</ul>
<div class="tabpanels">
<div role="tabpanel" aria-labelledby="tab-1" id="tabpanel-1">
<!-- タブパネルの中身 -->
</div>
<div role="tabpanel" aria-labelledby="tab-2" id="tabpanel-2">
<!-- タブパネルの中身 -->
</div>
</div>
</div>
スクリプト無効環境では、見た目はともかく機能的には「タブ」ではないため、role属性を最初からHTMLに書いておくのは良くないかも知れません。ただ、個人的にはスクリプトで動的に設定するのは面倒なのと、仮に良くないとしても現実的にユーザーにとって大きな支障にはならないと思うのでHTMLに書いちゃっています。
スクリプトが有効な時はアンカー機能は必要ないので、 JavaScript で href 属性を除去します。つまり、
<ul role="tablist">
<li role="tab" aria-controls="tabpanel-1" id="tab-1"><a href="#tabpanel-1">タブ1</a></li>
<li role="tab" aria-controls="tabpanel-2" id="tab-2"><a href="#tabpanel-2">タブ2</a></li>
</ul>
は以下のようになります。
<ul role="tablist">
<li role="tab" aria-controls="tabpanel-1" id="tab-1"><a>タブ1</a></li>
<li role="tab" aria-controls="tabpanel-2" id="tab-2"><a>タブ2</a></li>
</ul>
タブ部分をクリック or タップ
次に必要な挙動を設定していきますが、これは言わずもがなですね。
タブ部分をクリックまたはタップすると、当該のタブパネルを表示、それ以外を非表示にします。
キーボード操作
当然ながらタブの選択操作はキーボードでも行えなければなりません。
設定するのはこんなところですかね。
- タブ部分で →, ↓ を押すと次のタブへ切り替え
- タブ部分で ←, ↑ を押すと前のタブへ切り替え
- タブ部分で Home を押すと最初のタブへ切り替え
- タブ部分で End を押すと最後のタブへ切り替え
- タブパネル内で Ctrl + ←, Ctrl + ↑ を押すと当該のタブへフォーカスが移る
また、そもそも先のコードのままではタブ部分に Tab キー操作でフォーカスが合いませんから、選択中のタブには tabindex="0" を指定します。
選択していないタブは tabindex 属性がない状態でも良いですが、動的に切り替えるため属性自体を付けたり消したりするのではなく、値だけ変える方が良いと思うので "-1" を指定します。
<ul role="tablist">
<li role="tab" aria-controls="tabpanel-1" id="tab-1" tabindex="0"><a>タブ1</a></li>
<li role="tab" aria-controls="tabpanel-2" id="tab-2" tabindex="-1"><a>タブ2</a></li>
</ul>
WAI-ARIA のステート属性
必須ではありませんが、選択中、表示中の状態を表す属性を設定するとより良いでしょう。これも tabindex と同じく JavaScript で動的に変更しますが、1番目のタブを選択している場合、その結果はこんな感じになります。
<div class="tab_module">
<ul role="tablist">
<li role="tab" aria-controls="tabpanel-1" aria-selected="true" id="tab-1" tabindex="0"><a>タブ1</a></li>
<li role="tab" aria-controls="tabpanel-2" aria-selected="false" id="tab-2" tabindex="-1"><a>タブ2</a></li>
</ul>
<div class="tabpanels">
<div role="tabpanel" aria-labelledby="tab-1" aria-hidden="false" id="tabpanel-1">
<!-- タブパネルの中身 -->
</div>
<div role="tabpanel" aria-labelledby="tab-2" aria-hidden="true" id="tabpanel-2">
<!-- タブパネルの中身 -->
</div>
</div>
</div>
タブパネルの下側にもタブがあるケース
タブパネルが巨大な場合、中身を最後まで読んでから上にスクロールを戻して次のタブを選択するのは面倒です。スマートフォンのような小さい端末の場合はとくに。
その場合、下側にもタブを置くことがあります。
こちらのタブを操作したときは、単にタブパネルを切り替えるだけだとスクロール位置は下側のままであり、ユーザーが手動で次のタブパネルの最上部へスクロールしなければなりません。
それはあんまりなので自動でスクロールさせましょう。Element.scrollIntoView()(developer.mozilla.org
)という便利なメソッドがあるので、これを使うのが良いと思います。
タブパネルに向けたページ内リンクなどがある場合
タブパネルの中に複数の章があって、IDを設定してページ内リンクされているような場合。
<div role="tabpanel" aria-labelledby="tab-2" aria-hidden="true" id="tabpanel-2">
<p><a href="#tab-2_1">見出し1</a> <a href="#tab-2_2">見出し2</a></p>
<section id="tab-2_1">
<h2>見出し1</h2>
<p>本文1</p>
</section>
<section id="tab-2_2">
<h2>見出し2</h2>
<p>本文2</p>
</section>
</div>
この場合、 #tab-2_1 のハッシュが付いたURLがSNSなどでシェアされ、直接飛んで来られることも考えられます。
しかし、当該タブがデフォルトで非表示だと、表示上は見えないのにブラウザがその位置にスクロールしてしまうため、ページロード表示が変な位置になってユーザーが混乱してしまいます。
なので、
- URLにハッシュが指定されている
- document.getElementById(location.hash.substring(1)) な要素がどこかのタブパネルの中に存在する
- そのタブパネルがデフォルトでは非表示になっている
という条件がすべて当てはまる場合、強制的にそのタブパネルを表示状態にする必要があります。
以上が、だいたいどんなサイトにも含めておいて損はないタブの機能です。
他にも要件次第ですが、選択されたタブ情報をストレージや Cookie に記憶しておき、次回訪問時に選択状態にしておけるようにしたり、タブパネル部分のスワイプ操作で切り替えられるようにするといった機能が求められることも考えられます。
いやあ、タブって奥が深いですね。