モーダルを自作すると、画面中央への配置だけでなく、背景操作の抑制、フォーカス移動、Escキー、閉じた後のフォーカス復帰まで考える必要があります。HTMLの<dialog>要素を使うと、モーダルに必要な土台をブラウザ標準の仕組みで作れます。
この記事では、showModal()、form method="dialog"、::backdropを使い、確認モーダルをHTML・CSS・JavaScriptで実装します。
dialog要素を使うメリット
- JavaScriptから
showModal()でモーダルとして開ける - モーダル表示中は背景側を操作しにくい状態にできる
- Escキーによるキャンセル操作を標準の流れに乗せられる
::backdropで背景オーバーレイを装飾できるform method="dialog"でボタンの値を返しながら閉じられる
完成コード
HTML
<main class="demo-card">
<img class="nines-logo" src="https://kjnine.com/wp-content/themes/kjnines_theme/images/svg2/nines_logo.svg" alt="NINES">
<p class="eyebrow">NATIVE MODAL</p>
<h1>dialog要素で確認モーダル</h1>
<button class="open-button" type="button">削除内容を確認</button>
<dialog id="confirmDialog" aria-labelledby="dialogTitle">
<form method="dialog">
<p class="eyebrow">CONFIRM</p>
<h2 id="dialogTitle">この項目を削除しますか?</h2>
<p>削除後は元に戻せません。内容を確認して操作してください。</p>
<div class="dialog-actions">
<button value="cancel">キャンセル</button>
<button class="danger" value="delete">削除する</button>
</div>
</form>
</dialog>
<p id="result" aria-live="polite">まだ操作されていません。</p>
</main>CSS
* {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
padding: 24px;
background: #f4f6f9;
color: #111;
font-family: system-ui, sans-serif;
}
.demo-card {
position: relative;
width: min(100%, 560px);
padding: 40px;
border: 1px solid #d9dee8;
background: #fff;
box-shadow: 0 20px 50px rgb(18 33 63 / 10%);
}
.nines-logo {
position: absolute;
top: 18px;
right: 18px;
width: 58px;
opacity: 0.16;
}
.eyebrow {
color: #0057ff;
font-size: 0.78rem;
font-weight: 800;
letter-spacing: 0.14em;
}
button {
min-height: 44px;
padding: 10px 16px;
border: 1px solid #111;
background: #fff;
color: #111;
cursor: pointer;
font: inherit;
font-weight: 700;
}
.open-button,
.danger {
border-color: #0057ff;
background: #0057ff;
color: #fff;
}
dialog {
width: min(calc(100% - 32px), 480px);
padding: 0;
border: 0;
background: transparent;
}
dialog form {
padding: 30px;
border-left: 5px solid #0057ff;
background: #fff;
box-shadow: 0 24px 80px rgb(0 0 0 / 28%);
}
dialog::backdrop {
background: rgb(5 12 24 / 72%);
backdrop-filter: blur(3px);
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 24px;
}
@media (prefers-reduced-motion: no-preference) {
dialog[open] {
animation: dialog-in 180ms ease-out;
}
@keyframes dialog-in {
from {
opacity: 0;
transform: translateY(12px) scale(0.98);
}
}
}JavaScript
const dialog = document.querySelector('#confirmDialog');
const openButton = document.querySelector('.open-button');
const result = document.querySelector('#result');
openButton.addEventListener('click', () => {
dialog.showModal();
});
dialog.addEventListener('close', () => {
result.textContent = dialog.returnValue === 'delete'
? '削除を選択しました。'
: '操作をキャンセルしました。';
});show()ではなくshowModal()を使う
dialog.show()は通常の非モーダル表示、dialog.showModal()はモーダル表示です。ユーザーに回答を求める確認画面ではshowModal()、画面の端に補助情報を残すような用途では非モーダル表示を検討します。
form method="dialog"の仕組み
ダイアログ内のフォームへmethod="dialog"を付けると、送信時にページ遷移せずダイアログを閉じられます。押されたボタンのvalueはdialog.returnValueから取得できるため、キャンセルと実行を分けられます。
閉じる方法を整理する
| 操作 | 使うもの | 用途 |
|---|---|---|
| ボタンで閉じる | form method="dialog" | 確認・キャンセルの選択 |
| JavaScriptで閉じる | dialog.close(value) | 処理完了後など任意のタイミング |
| Escキー | cancelイベント | キーボードによるキャンセル |
| 閉じた後を処理 | closeイベント | returnValueの確認や画面更新 |
背景クリックで閉じる場合の注意
背景クリックで閉じる挙動は必須ではありません。重要な同意や破壊的操作では、誤操作を避けるため明示的なボタンだけにする方が安全です。追加する場合も、ダイアログ内部のクリックと背景のクリックを正しく区別します。
アクセシビリティで確認すること
- 見出しを用意し、
aria-labelledbyで関連付ける - ボタン文言を「はい/いいえ」だけでなく操作内容が分かる表現にする
- 色だけで危険操作を示さず、テキストでも説明する
- Escキーで閉じて問題ない設計かを確認する
- 閉じた後に開いたボタンへフォーカスが戻るか実機で確認する
ユーザーの判断を止めて回答を求めるならdialog、補足メニューや一時的な情報表示ならPopover APIが向いています。
CodePen埋め込み用エリア
HTML、CSS、JavaScriptをそれぞれのパネルへ貼り付ければ動作します。まずはvalue="delete"を別の操作名へ変え、returnValueがどう変わるか試してください。
あわせて読みたい
参考資料
まとめ
<dialog>を使うと、確認モーダルの土台を少ないコードで作れます。showModal()で開き、method="dialog"で結果を返し、::backdropで背景を整えるのが基本です。独自のdivだけで作る前に、標準要素で要件を満たせるか確認しましょう。