以前公開した WEBサイトのアクセシビリティ対応では、全体的な考え方について触れました。
今回はその中でも利用頻度の高いダイアログ(モーダルウィンドウ)に焦点を当てて解説します。
ダイアログは、入力フォームの送信確認や画像の拡大表示など、Webサイトでよく利用される便利な仕組みです。
しかし、アクセシビリティ対応が不十分なまま実装されるケースも少なくありません。
特にキーボードだけで操作するユーザーや、スクリーンリーダーを利用するユーザーにとっては、誤ったダイアログの実装が操作不能や情報取得の妨げにつながることもあります。
この記事では、アクセシビリティ対応をしたダイアログ(モーダルウィンドウ)の実装ポイントを紹介していきます。
外側の要素のHTMLを書いていこう
まずはコンテンツや、ダイアログを開くトリガーになるボタンを設置しましょう。
1 2 3 4 5 6 |
<main id="pageContent"> <!-- モーダルを開くトリガー --> <button type="button" id="openModalBtn" aria-haspopup="dialog" aria-controls="sampleModal"> 画像を拡大 </button> </main> |
ポイント
『aria-haspopup=”dialog”』をつける
『aria-haspopup=”dialog”』をbuttonにつけます。こうすることでこのボタンはダイアログ(モーダル)を開くものだと支援技術に伝えることができます。
『aria-controls=””』をつける
どの要素を開くか(対応するダイアログのID)を示します。今回の場合はモダールの要素に『id=”sampleModal”』と記述して、『aria-controls=”sampleModal”』で関連付けをしました。
ダイアログ本体のHTMLを書いていこう
次にダイアログ本体のHTMLを書いていきましょう。
1 2 3 4 5 6 7 8 9 10 |
<div id="sampleModal" role="dialog" aria-modal="true" aria-labelledby="dialogTitle" aria-describedby="dialogDesc" hidden> <div class="modal-content"> <h2 id="dialogTitle">拡大画像</h2> <p id="dialogDesc">拡大した画像の表示です</p> <img src="sample-large.jpg" alt="拡大された画像"> <button type="button" class="closeBtn" aria-label="閉じる">×</button> </div> </div> |
ポイント
『role=”dialog”』をつける
ダイアログになるdivに対して『role=”dialog”』を付けましょう。これで支援技術などに「オーバーレイを使用して、ページコンテンツの上に表示されるようなダイアログやモーダルウィンドウ」、と認識してくれます。
『aria-modal=”true”』を付ける
『aria-modal=”true”』を付けましょう。先ほどの「role=”dialog”」と何が違うの?と思うかもしれませんが、こちらでは背景の扱いについて指定をします。
「このダイアログが開いている間、背景は無効(操作不可)で、現在の操作対象はダイアログだけ」ということを伝えてくれます。
『aria-labelledby=””』を付ける
こちらはタイトル要素とダイアログ全体を関連づけるために使用します。ダイアログのタイトル要素と、ダイアログ全体を関連付けることで、スクリーンリーダーがダイアログの内容を正しく認識できます。
タイトル要素にはidを付与し、そのidをダイアログのaria-labelledbyに指定します。
今回の場合はモダールの中のタイトル要素に『id=”dialogTitle”』と記述して、『aria-labelledby=”dialogTitle”』で関連付けをしました。
『aria-describedby=””』を付ける
先ほどのaria-labelledbyと似ていますが、こちらは具体的な説明をスクリーンリーダーに読ませるために使います。説明部分にidを付与し、そのidをダイアログの aria-describedbyに指定します。
今回は『id=”dialogDesc”』と記述して、『aria-describedby=”dialogDesc”』で関連付けをしました。
tabキーなどでのフォーカスでの制御をしやすくするために、閉じるボタンをbutton要素で置きます。
また、『×』のように記号のみでボタンの挙動を表している場合は、スクリーンリーダーにどのような動きをするボタンか読ませるために『aria-label』でボタンの説明を加えましょう。
『閉じる』とテキストで元から書いてある場合は不要です。
最初からダイアログを隠すためにhiddenをつけます。
ここについてはJavaScriptやCSSで後程細かい設定をします。
JavaScriptを書いていこう
次にアクセシビリティ対応をしたJavaScriptを記述していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
const openBtn = document.querySelector('#openModalBtn'); const modal = document.querySelector('#sampleModal'); const closeBtn = modal.querySelector('.closeBtn'); const mainContent = document.querySelector('#pageContent'); let lastFocusedElement = null; // ダイアログを開く openBtn.addEventListener('click', () => { lastFocusedElement = document.activeElement; modal.removeAttribute('hidden'); mainContent.setAttribute('inert', ''); closeBtn.focus(); }); // ダイアログを閉じる function closeModal() { modal.setAttribute('hidden', ''); mainContent.removeAttribute('inert'); lastFocusedElement.focus(); } closeBtn.addEventListener('click', closeModal); // Escキー対応 + フォーカストラップ modal.addEventListener('keydown', e => { if (e.key === 'Escape') { closeModal(); } if (e.key === 'Tab') { const focusableEls = modal.querySelectorAll( 'a[href], button:not([disabled]), input, textarea, select, [tabindex]:not([tabindex="-1"])' ); const firstEl = focusableEls[0]; const lastEl = focusableEls[focusableEls.length - 1]; if (e.shiftKey && document.activeElement === firstEl) { e.preventDefault(); lastEl.focus(); } else if (!e.shiftKey && document.activeElement === lastEl) { e.preventDefault(); firstEl.focus(); } } }); |
ポイント
初期非表示の設定をする
ページ読み込み時にモーダルが見えないよう、hidden属性を使って非表示にしておきます。背景を無効化する
モーダルを開いたら背景コンテンツ(今回だと#pageContent部分)に inertを付与して、誤操作や不要なフォーカス移動を防ぎます。Escキー対応を行う
モーダルは閉じるボタンだけでなく、Escキーでも閉じられるようにするとユーザーが迷いません。フォーカストラップを行う
フォーカストラップとは、Webサイトをキーボードで操作する際に、フォーカスをとある領域からはみ出さないようにすることです。Tabキー操作でフォーカスがモーダル外に逃げないよう制御し、操作対象をモーダル内に限定します。
フォーカスの返却をする
モーダルを閉じたら、開いたときのボタン(今回だと「画像を拡大」ボタン)にフォーカスを戻し、操作の流れを途切れさせないようにします。CSSを書いていこう
CSSについては、特にこれといったアクセシビリティ対応に関係するものはありません。普通にダイアログの表示用に記述をしていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
/* 背景のオーバーレイ */ #sampleModal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background-color: rgba(0, 0, 0, 0.5); z-index: 1000; } /* ダイアログ本体 */ #sampleModal .modal-content { background: #fff; padding: 1.5rem; border-radius: 6px; max-width: 90%; max-height: 90%; overflow: auto; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); } /* 閉じるボタン */ #sampleModal .closeBtn { position: absolute; top: 1rem; right: 1rem; font-size: 1.5rem; background: none; border: none; cursor: pointer; } /* hidden属性用(初期非表示) */ [hidden] { display: none !important; } |
完成!
これで無事完成しました!
See the Pen
ダイアログ(モーダルウィンドウ)のアクセシビリティ対応 by kkdd (@kk8kk)
on CodePen.
大きな画面はこちら
正直パッと見た感じはわかりにくいとは思うのですが、開発者モードで見るとスクリーンリーダー用の切り替えがされていたり、キーボードのみで操作ができるような配慮がされています。
まとめ
長すぎてよくわからない、という方もいると思うので要点をまとめました。とりあえず上のコードをコピーしたうえで設定をしてみるといろいろわかってくるかなと思います。
-
適切な役割付与をする
role=”dialog” と aria-modal=”true”などでダイアログであることを明示します。
-
タイトルと説明を加える
aria-labelledbyとaria-describedbyで内容を正しく伝えましょう。
-
初期非表示の設定をする
hidden属性などでページロード時に表示されないようにしましょう。
-
背景の操作無効化をする
ダイアログ表示中は背景にinertを付けるなどして、誤操作を防止しましょう。
-
フォーカス管理をする
ダイアログ内にフォーカスを制限(フォーカストラップ)、閉じたら元の要素に戻しましょう。
-
閉じるボタンの工夫をする
閉じるボタンについてはクリックまたはEscキーで閉じられるようにしましょう。