WAI-ARIAに対応したタブを作ってみた(作成方法)【ES6】
最近業務でタブを久々に実装することがあったのでWAI-ARIAに対応ものを作成してみました。
この記事ではその作成したタブをご紹介します。
目次
WAI-ARIAに対応したタブを作ってみた【ES6】
実装するのも
- WAI-ARIAに対応
- 複数タブの設置に対応
デモ
以下、ソースコードの紹介になります。
HTML
<ul class="js-tab-list tab-list" role="tab-list">
<li class="tab-list__item">
<button class="js-tab-button tab-button" type="button" role="tab" aria-controls="tab-panel-1" aria-selected="true">
タブ1
</button>
</li>
<li class="tab-list__item">
<button class="js-tab-button tab-button" type="button" role="tab" aria-controls="tab-panel-2" aria-selected="false">
タブ2
</button>
</li>
<li class="tab-list__item">
<button class="js-tab-button tab-button" type="button" role="tab" aria-controls="tab-panel-3" aria-selected="false">
タブ3
</button>
</li>
</ul><!-- tab-list -->
<div class="js-tab-panel-wrapper tab-content">
<div id="tab-panel-1" class="js-tab-panel tab-panel" role="tab-panel" aria-hidden="false">
<p>タブ1の内容が入ります。</p>
</div>
<div id="tab-panel-2" class="js-tab-panel tab-panel" role="tab-panel" aria-hidden="true">
<p>タブ2の内容が表示されます。</p>
</div>
<div id="tab-panel-3" class="js-tab-panel tab-panel" role="tab-panel" aria-hidden="true">
<p>タブ3の内容です。</p>
</div>
</div>
role属性とaria属性を使用します。
タブのボタンエリアを.tab-list
タブの内容を表示するエリアを.tab-content
.tab-list
にタブのリストと分かるようにrole="tab-list"
タブのボタンにはタブ本体と分かるようにrole="tab"
タブとタブ内容を紐付けるためにaria-controls="tab-panel-1"
とタブ内容のidを指定
タブが選択状態にあるかを示すためにaria-selected="true"
未選択ならaria-selected="false"
タブ内容にはrole="tab-panel"
そのタブ内容が表示状態にあるかを示すためにaria-hidden="false"
非表示ならaria-hidden="true"
CSS(SCSS)
//タブ ボタン
.tab-list {
display: flex;
}
.tab-list__item {
flex: 1;
&:not(:first-child) {
margin-left: 5px;
}
}
.tab-button {
width: 100%;
height: 100%;
padding: 10px;
background: #eee;
cursor: pointer;
&[aria-selected="true"] {
background: #3ac8ff;
}
}
//===============タブ ボタン
//タブ パネル
.tab-content {
margin-top: 20px;
}
.tab-panel {
padding: 20px 15px;
border: 1px solid #eee;
&[aria-hidden="true"] {
display: none;
}
}
//===============タブ パネル
スタイルはサンプル用ですがポイントは以下です
aria-selected
属性とaria-hidden
属性で表示・非表示状態のスタイルを切り替えます。
JavaScript
class Tab {
constructor() {
this.tab_button = document.querySelectorAll('.js-tab-button');
}
init() {
this.attachEvent();
}
attachEvent() {
for(const button of this.tab_button) {
button.addEventListener('click', (e) => {
let id = button.getAttribute('aria-controls');
this.hideContents(id, e);
this.showContent(id, e);
});
}
}
hideContents(id, e) {
//クリックしたタブ、パネルの親を取得
let button_parent = e.target.closest('.js-tab-list');
let panel_parent = document.querySelector(`#${id}`).closest('.js-tab-panel-wrapper');
//表示状態のボタン、パネルの状態を取得
let active_button = button_parent.querySelector('.js-tab-button[aria-selected="true"]');
let active_panel = panel_parent.querySelector('.js-tab-panel[aria-hidden="false"]');
//タブボタン
active_button.setAttribute('aria-selected', 'false');
//タブパネル
active_panel.setAttribute('aria-hidden', 'true');
}
showContent(id, e) {
//タブボタン
e.target.closest('.js-tab-button').setAttribute('aria-selected', 'true');
//タブパネル
document.querySelector(`#${id}`).setAttribute('aria-hidden', 'false');
}
}
const tab = new Tab();
tab.init();
class構文で記述しています。
少々長いですが、ポイントは以下となります。
- タブボタンにクリックイベントを付与
- 複数タブの設置に対応するために親要素を取得
- 表示している要素を非表示にしてから別のタブを表示
タブボタンをクリックすることで各要素のaria-selected
とaria-hidden
の値を切り替えます。
表示・非表示の処理は先程ご紹介したCSSで行っています。
クリックイベントを付与
for-of文でタブボタン(class.js-tab-button
)にクリックイベントを付与します。
タブをクリックしたときにaria-controls
の値を取得
※タブボタンとタブ内容を紐付けるIDです
タブの切り替え処理
非表示
複数タブの設置に対応するためにclosest
メソッドで親要素を取得して表示状態のタブを非表示にします。
表示
タブをクリックしたときに取得したID(aria-controls
の値)をもとに表示処理を行います。
注意点
親要素を取得するために使用しているclosest
メソッドはIEでは動きません。
ですが、対応するためのポリフィルがありますので以下をご確認ください。
最後に
これでWAI-ARIAに対応したタブを実装できました。
class属性だけでも実装は可能(そちら方が楽)ですが、アクセシビリティーの観点からこの方法で実装するのが良いと思います。
タブに限らずアコーディオンやハンバーガーメニューにも面倒かもしれませんがWAI-ARIAに対応したほうがいいのかなと思いました。
参考にさせて頂いた記事
https://ics.media/entry/17107/
https://to-memo.com/web/wai-aria_tab/