コラム / 制作会社・広告会社向け

dialog要素のモーダル実装|閉じ方とフォーカス復帰を検証

モーダルは、開くだけでなく閉じ方と元の作業位置へ戻る操作まで設計します。dialog要素のコード例と架空画面のPC・スマホ幅での試験から、確認できた動作と追加検収が必要な範囲を示します。

公開 最終確認 2026-11-12著者 GreenCoding
ノート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、スクリーンリーダー、拡大表示、長文や入れ子のモーダルは未検証です。対象ブラウザで背景へ移らないことと、戻り先を追加検収してから案件へ使います。

検収では、開くところから元の作業へ戻るまで試す

  1. マウスを使わずTabで開くボタンへ進み、EnterとSpaceで開きます。
  2. 最初の位置、内部の順序、逆方向の移動、背景が操作できないことを確認します。
  3. Escと画面上の閉じるボタンをそれぞれ試し、元の作業位置へ戻れるかを確認します。
  4. スマホ幅と拡大表示で、本文・閉じる操作が画面から消えないかを確認します。

GreenCodingへ実装を相談するときは、開閉前後の画面と対象端末、モーダルへ入れる情報、戻り先を共有してください。支給デザインからの実装や既存画面の修正について、必要な検証範囲と合わせて確認します。

仕様と設計の参照資料

標準仕様はWHATWGのdialog要素、要素の使い方はMDNのdialog解説、操作の検収観点はW3Cのモーダルダイアログ設計パターンを参照しました。確認日は2026年11月12日です。記事内のコードは説明用で、公開ページ上では実行しません。