コラム / 制作会社・広告会社向け
dialog要素のモーダル実装|閉じ方とフォーカス復帰を検証
モーダルは、開くだけでなく閉じ方と元の作業位置へ戻る操作まで設計します。dialog要素のコード例と架空画面のPC・スマホ幅での試験から、確認できた動作と追加検収が必要な範囲を示します。

ページの上に案内を重ねて表示するモーダルは、開くボタンだけでなく、閉じ方と元の位置へ戻る操作まで設計します。HTMLのdialog要素を使う場合も、見た目が整った時点で完成とはせず、キーボードと狭い画面で確かめます。
制作会社様の実装・検収担当に向けて、展示会ページから来場条件を開く架空の例を作りました。実在する催事や申込フォームではなく、送信・保存を行わないローカル試験です。既存記事のリンクとボタンの使い分けから一歩進め、開いた後の操作を扱います。
長い説明や必須条件は、通常のページにも置く
モーダルは、元のページを一時的に操作できなくして補足を読む場面に向きます。展示会の日時、申込条件、費用など、読むことが欠かせない情報をモーダルの中だけへ隠す設計は避けます。長い規約や共有・印刷する説明は、独立したページも候補になります。
今回の例は、同じページへ戻って作業を続ける短い来場案内です。画面設計には、開く部品、最初に選択される部品、閉じるボタン、閉じた後の位置を書き込みます。背景を暗くする指定だけでは、背景の操作を止めたことになりません。
divの表示切り替えだけでは、操作が不足する
// 問題例:見た目は開くが、モーダルの操作は備わらない
openButton.addEventListener("click", () => {
panel.hidden = false;
});
試作の問題例では、buttonからdivを表示しても、フォーカスは開くボタンに残りました。フォーカスは、いまキーボード操作の対象になっている位置です。Escを押しても閉じず、背景のリンクも使える状態でした。role=”dialog”を付けても、これらの動作が自動で完成するわけではありません。
dialog要素に、名前と閉じる操作を用意する
修正例は、dialogをshowModal()で開きます。open属性を付けるだけの場合とは異なり、モーダルとして背景の操作を制限します。ここでは短い説明なので、autofocusを付けた「閉じる」ボタンを初期位置にしました。長い文章では冒頭の見出し等へ移す設計も検討します。
<button id="open" type="button">来場条件を確認する</button>
<dialog id="conditions" aria-labelledby="dialog-title">
<h2 id="dialog-title">来場前の確認事項</h2>
<p>ここへ短い案内を置きます。</p>
<button id="close" type="button" autofocus>閉じる</button>
</dialog>
const opener = document.querySelector("#open");
const panel = document.querySelector("#conditions");
opener.addEventListener("click", () => panel.showModal());
document.querySelector("#close")
.addEventListener("click", () => panel.close());
panel.addEventListener("close", () => opener.focus());
aria-labelledbyは、ダイアログの名前を画面内の見出しと結び付けます。dialog自体にtabindexは付けません。この例は開くボタンが残る前提です。画面更新でボタンを消す場合は、戻す位置を別に設計します。Escを無効にする処理や、背景クリックだけに頼る閉じ方は追加していません。
最初の試験では末尾からTabを押すとブラウザ側へ移る場面があったため、この固定したサンプルには端の折り返しも実装しました。対象は常時表示するリンクと有効なボタンです。入力欄や非表示の部品が増える案件では、この抽出条件をそのまま流用せず再設計します。
panel.addEventListener("keydown", event => {
if (event.key !== "Tab") return;
const items = [...panel.querySelectorAll(
"a[href], button:not(:disabled)"
)];
if (!items.length) return;
const first = items[0], last = items[items.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault(); last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault(); first.focus();
}
});
dialog {
box-sizing: border-box;
width: min(34rem, calc(100% - 2rem));
max-height: calc(100dvh - 2rem);
overflow: auto;
}
dialog::backdrop { background: rgb(0 0 0 / .55); }
:focus-visible { outline: 3px solid #ac7800; outline-offset: 4px; }
高さを画面内に収め、内容が増えたら内部でスクロールできるようにします。上のコードは要点の抜粋で、試作用には余白、文字、配色、48px以上のボタン高も指定しました。背景ページのスクロール固定は別の要件です。このCSSだけで全端末のスクロール挙動を保証するものではありません。
PC・スマホ幅で確かめた結果と未確認の範囲
2026年11月12日、Mac上のIAB(Chrome 157.0.0.0のUA表示)で、幅1280pxと390pxを確認しました。修正例ではEnter・Spaceで開き、閉じるボタンへフォーカスが移りました。試作用に入れた内部リンクからTabで閉じるボタンへ、逆向きにはShift+Tabで戻れました。Escと閉じるボタンのどちらでも終了し、開くボタンへ戻りました。
| 確認対象 | 今回の結果 |
|---|---|
| PC幅1280px | ダイアログ幅544px。本文と閉じるボタンを表示。 |
| スマホ幅390px | ダイアログ幅348px。ページの横はみ出しなし。 |
| キー操作 | 初期位置、内部リンクとの往復、Esc、ボタン終了、復帰を確認。 |
折り返しの追加後は、末尾のボタンからTabで先頭のリンクへ、逆向きにはShift+Tabで末尾へ戻ることをPC・スマホ幅で確認しました。実機タッチ、Safari・Firefox、スクリーンリーダー、拡大表示、長文や入れ子のモーダルは未検証です。対象ブラウザで背景へ移らないことと、戻り先を追加検収してから案件へ使います。
検収では、開くところから元の作業へ戻るまで試す
- マウスを使わずTabで開くボタンへ進み、EnterとSpaceで開きます。
- 最初の位置、内部の順序、逆方向の移動、背景が操作できないことを確認します。
- Escと画面上の閉じるボタンをそれぞれ試し、元の作業位置へ戻れるかを確認します。
- スマホ幅と拡大表示で、本文・閉じる操作が画面から消えないかを確認します。
GreenCodingへ実装を相談するときは、開閉前後の画面と対象端末、モーダルへ入れる情報、戻り先を共有してください。支給デザインからの実装や既存画面の修正について、必要な検証範囲と合わせて確認します。
仕様と設計の参照資料
標準仕様はWHATWGのdialog要素、要素の使い方はMDNのdialog解説、操作の検収観点はW3Cのモーダルダイアログ設計パターンを参照しました。確認日は2026年11月12日です。記事内のコードは説明用で、公開ページ上では実行しません。

