本文へ移動
ご相談

モーダルを開いても背後にフォーカスが残る

ダイアログが表示されても、Tabキーで移動する先は背後のページです。

WCAG 2.4.3WCAG 2.1.2 確認方法 キーボードスクリーンリーダーズーム強制配色 エージェント向けMarkdown
問題あり フォーカスが背後に残る。
修正後 フォーカスが中に入る。
同じ操作部品の、修正前と修正後。
01

問題

ダイアログは表示されているのに、キーボード操作は背後のページに残っています。

場面 Tabキーで、背後のページを移動してしまう。 詳しく読む閉じる

詳細編集のダイアログを開くと、ページの上に重なって表示されます。しかしTabキーを押すと、ダイアログ内ではなく、背後のリンクへフォーカスが移動します。

確認できる問題 見た目はモーダルでも、動作が伴っていない。 詳しく読む閉じる

見た目はモーダルでも、フォーカスの移動が適切に管理されていません。背後のページを操作できてしまい、ダイアログが開いたことに気づけない場合があります。

利用者への影響 キーボードやスクリーンリーダーの利用者が、別の場所を操作してしまう。 詳しく読む閉じる

キーボードやスクリーンリーダーの利用者が、ダイアログの外を操作してしまったり、必要な入力に気づけなかったりします。閉じた後も、操作を再開する位置が分からなくなるおそれがあります。

自動チェックとAIの限界 roleやaria-modalだけでは、フォーカスは移動しない。 詳しく読む閉じる

role と aria-modal を設定するだけでは、フォーカスは移動せず、背後のページの操作も防げません。初期状態のDOMだけでは、開閉時、ダイアログ内の移動、閉じた後のフォーカスが適切か判断できません。

02

試してみる

ダイアログを開いてから、Tabを押してください。

ダイアログを開き、TabキーとEscapeキーを押してください。

再現デモです。「問題あり」は意図的にアクセシブルでない状態にしています。

03

修正方法

HTML標準のdialogをshowModal()で開き、閉じた後のフォーカス位置を確認します。

  1. 1aria-labelledbyでダイアログに名前を付ける。
  2. 2最初のフォーカスは作業の起点か、最も影響の小さい操作に置く。
  3. 3閉じたら開いたボタンへ、ない場合は適切な位置へフォーカスを戻す。
修正前 見た目だけのモーダル

html

<div class="overlay" hidden>  <div class="modal">    <p class="modal-title">詳細を編集</p>    <button onclick="closeModal()">閉じる</button>  </div></div>

js

function openModal() {  document.querySelector('.overlay').hidden = false;}
修正後 HTML標準のdialog要素

html

<button type="button" id="edit-trigger">詳細を編集</button> <dialog id="edit-dialog" aria-labelledby="edit-title">  <h2 id="edit-title">詳細を編集</h2>  <form method="dialog">    <label for="display-name">表示名</label>    <input id="display-name" name="name">    <button value="save">保存</button>    <button value="cancel">キャンセル</button>  </form></dialog>

js

const trigger = document.getElementById('edit-trigger');const dialog = document.getElementById('edit-dialog');trigger.addEventListener('click', () => dialog.showModal());

js

// 閉じた後に開くボタンが残っていない場合は、移動先を決めます。dialog.addEventListener('close', () => {  if (!trigger.isConnected) document.getElementById('item-list')?.focus();});
この修正で直る理由

オーバーレイを表示しても、変わるのは見た目だけです。フォーカスは開くボタンに残り、Tabキーは背後のページを移動し、Escapeキーも効かず、ダイアログには名前もありません。

showModal() は、ダイアログ内へフォーカスを移し、背後のページを操作できない状態にし、Escapeキーで閉じられるようにします。method="dialog" のフォーム内のボタンでも閉じます。現在のブラウザーは、閉じた後に開いた要素へフォーカスを戻しますが、画面の再描画でその要素が置き換わることもあるため、必ず確認してください。

実装の判断

長いダイアログは、tabindex="-1" の見出しを初期位置にする場合があります。削除など取り消せない操作では、キャンセルなど安全な選択肢に最初のフォーカスを置き、そのボタンに autofocus を指定します。ダイアログを入れ子にする場合は、閉じた後のフォーカス移動も確認してください。

dialog要素を使えない場合も、独自のモーダルで同じ動作が必要です。role="dialog" と aria-modal="true" と名前を設定し、開いたらフォーカスを移し、背後のページを inert にし、Escapeキーで閉じ、閉じたらフォーカスを戻します。

04

修正の確認

マウスを使わない5つの確認

5件中0件を確認済み

OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。共通のダイアログコンポーネントを変更した際は、再度確認します。

制限

このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。保存や入力検証の処理は省略しています。実際に使用する支援技術で、操作全体を確認してください。

よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。

参考資料: www.w3.org

コードを更新しても、使いやすさを保つために

ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。

再テストの進め方