モーダルUIをシンプルにできる! 進化を続けるHTMLのdialog要素

HTMLのdialog要素を利用すると、モーダルダイアログをシンプルに実装できます。dialog要素は、2022年頃から主要ブラウザで利用可能です。

モーダルとは、表示している間は他の操作を禁止し、ある操作が完了するまで先に進めなくするUIを指します。dialog要素の登場以前は、次のような制御が必要で複雑でした。

  • 画面の最前面にモーダルUIを表示する
  • モーダルUIが表示されている間、ほかの操作を制限する
  • モーダルUI開閉時に、フォーカス位置を調整する

こうした複雑さについては記事『HTMLでモーダルUIを作るときに気をつけたいこと』でも解説していますので、あわせて参考にしてください。

dialog要素を利用すれば、HTMLが標準で提供する仕組みによって、シンプルかつ一貫した方法でモーダルダイアログを扱えるようになります。本記事では、dialog要素の基本的な使い方を紹介します。

本記事で紹介すること:

  • dialog要素の基本的な使い方
  • closedbyautofocusなどの便利な属性
  • CSSでスタイルを適用する例

dialog要素の使い方

まずは次の作例とコードをご覧ください。この作例は、dialog要素で実装したモーダルダイアログです。ボタンを押下することでモーダルダイアログの表示を切り替えます。

<button id="showDialog">モーダルダイアログを開く</button>

<dialog>
  <p>ダイアログボックスの説明文が入ります。</p>
  <button id="closeDialog">閉じる</button>
</dialog>
const dialog = document.querySelector("dialog");

const showButton = document.querySelector("#showDialog");
showButton.addEventListener("click", () => {
  dialog.showModal(); // モーダルダイアログを開く
});

const closeButton = document.querySelector("#closeDialog");
closeButton.addEventListener("click", () => {
  dialog.close(); // モーダルダイアログを閉じる
});

モーダルダイアログを開くボタンが押下されると、ボタンの上にdialog要素が重なって表示されます。表示されている間は、モーダルダイアログを開くボタンは押下できません。

dialog要素は、標準仕様で次の機能を提供します。

  • ダイアログボックスを画面の最前面に表示する
  • 子要素をボックス内のコンテンツとして表示する
  • showModal() / close()などの開閉用API
  • 表示されている間はほかの操作を制限し、閉じたら制限を解除する

dialog要素を利用することで、最小限のマークアップとJavaScriptだけで、モーダルダイアログを作成できます。ひととおりの動作を確認したところで、開き方や閉じ方など基本的な使い方を順番に見ていきます。

開き方

dialog要素は、デフォルトでは非表示になっています。表示方法は次の3種類があります。

  • JavaScriptでshowModal()メソッドを実行する
  • JavaScriptでshow()メソッドを実行する
  • HTMLであらかじめopen属性を指定する(ページが読み込まれたらdialog要素を表示する)

3種類のうち、showModal()メソッドのみがdialog要素をモーダルダイアログとして表示します。show()メソッドまたはopen属性で開くと、dialog要素は非モーダルダイアログとして表示されます。非モーダルとは、画面に重ねて表示してもほかの操作を制限しないUIを指します。

dialog要素の使い方」の段落で紹介した作例では、showModal()メソッドを実行してdialog要素を表示しています。

const dialog = document.querySelector("dialog");

const showButton = document.querySelector("#showDialog");
showButton.addEventListener("click", () => {
  dialog.showModal(); // dialog要素を開く
});

閉じ方

表示したdialog要素を非表示にするには、次の方法が考えられます。

  • JavaScriptでclose()メソッドを実行する
  • ESCキーを押下するなど、ブラウザやOSが用意している閉じる操作を行う

dialog要素には、何かしらの操作を行えるボタンが必要になります。操作のなかで要素を非表示にしたい場合はclose()メソッドを実行します。次のコード例は、閉じるためのボタンを配置し、押下されたらclose()メソッドが実行されます。

<dialog>
  <button>閉じる</button>
</dialog>
const dialog = document.querySelector("dialog");

/* 表示する処理は省略 */

const button = document.querySelector("button");
button.addEventListener("click", () => {
  dialog.close(); // dialog要素を閉じる
})

なお、close()メソッドには文字列を引数として渡せて、returnValueというプロパティから参照できます。詳しい使い方は次の段落で紹介します。

利用可能なイベント

dialog要素で利用できる主なイベントは、次の2種類です。

  • closedialog要素が閉じられるときに発火する
  • cancel:ブラウザやOSが用意している閉じる操作が指示されたときに発火する

2つのイベントは似ていますが、発火タイミングに違いがあることに注意してください。たとえば、close()メソッドでdialog要素が閉じられる場合は、closeイベントのみ発火します。それに対して、ESCキーの押下といった閉じる操作が行われた場合は、まずcancelイベントが発火され、その後closeイベントが発火します。

また、closeイベントの引数event.target.returnValueから、close()メソッドの引数を参照できます。押下されたボタンに応じて、閉じたあとの処理を切り替えたいときに役立ちます。次のコード例は、dialog要素にキャンセルと同意の2つのボタンを配置し、同意ボタンが押下されたときに追加の処理を行います。

<dialog>
  <button id="disagree">キャンセル</button>
  <button id="agree">同意する</button>
</dialog>
const dialog = document.querySelector("dialog");

// ⭐️closeイベントの追加
dialog.addEventListener("close", (event) => {
  if(event.target.returnValue === "agree") {
    // 同意するボタンが押下されたときの処理
  }
})

// キャンセルボタンが押下されたとき
const disagreeButton = document.querySelector("#disagree");
disagreeButton.addEventListener("click", () => {
  dialog.close("disagree"); // closeイベントに文字列 "disagree" を渡す
});

// 同意するボタンが押下されたとき
const agreeButton = document.querySelector("#agree"); 
agreeButton.addEventListener("click", () => {
  dialog.close("agree"); // closeイベントに文字列 "agree" を渡す
});

close()メソッドに引数を渡さない場合や、ESCキーが押下されてcloseイベントが呼ばれる場合は、returnValueプロパティは空文字("")を返します。

コラム:ほかの操作を制限する仕組み

showModal()メソッドで表示されるモーダルダイアログは、Top layerと呼ばれる特別な要素として表示されます。Top layerとしてdialog要素が表示されると、ほかの要素はクリックもフォーカスもできなくなり、ユーザーはダイアログボックスの操作に集中できます。なお、要素がTop layerとして表示されているかどうかは、Chromeの開発者ツールのからElementsパネルの#top-layerから確認できます。

Top layerの詳細は、MDNの以下のページを参考にしてください。

closedby属性

ここからはdialog要素を利用するうえで便利なHTML属性を紹介します。dialog要素とはサポート状況が少し異なるため、利用する際は注意してください。

dialog要素にはclosedby属性があり、モーダルの閉じやすさを調整できます。具体的には、ダイアログボックスの外側をクリックした際や、ESCキーを押下した際にモーダルを閉じるかどうかを制御できます。closedby属性で設定できる値は次の通りです。

  • any:ブラウザやOSの閉じる操作や、ダイアログボックスの外側を押下して閉じる(light dismissと呼ばれます)
  • closerequest:ブラウザやOSの閉じる操作で閉じる
  • none:標準操作には応答せず、close()メソッドでのみ閉じる(ESCキーでは閉じない)

なお、closedby属性が未指定の場合、ブラウザやOSの閉じる操作で閉じられます。次の作例では、closedbyの各設定を動作確認できます。プルダウンメニューからclosedby属性の値を変更できるので、試してみてください。

対応ブラウザ

closedby属性は、Chrome 134・Edge 134(2025年3月)、Firefox 141(2025年7月)で利用可能です。

autofocus属性

標準の動作では、dialog要素が表示されると最初にフォーカス可能な子要素にフォーカスが移動します。もしフォーカス対象の要素を変更したい場合は、autofocus属性を使います。

次の作例は、規約の同意を確認するモーダルダイアログです。キャンセルと同意の2つボタンがあり、同意ボタンにautofocus属性を指定しています。

<dialog>
  <p>規約に同意しますか?</p>
  <button>キャンセル</button>
  <button autofocus>同意する</button>
</dialog>

要素の標準の動作であれば、キャンセルボタンにフォーカスが移動しますが、作例は同意ボタンにフォーカスが移動することを確認できます。

対応ブラウザ

autofocus属性は、Chrome79・Edge 79(2020年1月)、Firefox 110(2023年2月)、macOS Safari 15.4(2022年3月)、iOS Safari 16.4(2023年3月)で利用可能です。

open属性で非モーダルダイアログの<input autofocus />をフォーカスした場合、2025年9月時点のiOS Safari 18.6では、仮想キーボードが表示されない仕様になっています。

dialog要素にスタイルを適用する

モーダルダイアログを実装するときは、デザインにあわせて独自のスタイルを適用することが多いです。次の作例は、削除確認ダイアログを想定してdialog要素にスタイルを適用しています。

独自のスタイルを適用する際は、次のポイントを押さえておくと実装がスムーズです。

  • ダイアログボックスの外側に表示される半透明のオーバーレイは、::backdrop疑似要素でスタイルを調整できます。
  • 開いた状態は、dialog[open]セレクターでスタイルを適用します。
  • 開閉時にアニメーションを設定する場合、dialog要素が非表示のときは display: noneとなるため、アニメーション用の初期スタイルを@starting-styleで指定します。

コラム:背面がスクロールできる問題について

モーダルの一般的な実装として、モーダル表示中に背面にあるコンテンツをスクロールしないようにする制御が必要となることが多いです。改善策として、dialog要素を開いているときはルート要素のスクロールをoverflowプロパティで抑制するといった方法が考えられます。

:root:has(dialog[open]) {
  overflow: hidden;
  scrollbar-gutter: stable;
}

※2025年9月時点では、上述したコードでもiOS Safari 18.6で完全に制御するのが難しいです。

まとめ

dialog要素の基本的な使い方を紹介しました。dialog要素を利用することで、標準の機能でシンプルにモーダルダイアログを実装できます。また、本記事では紹介しませんでしたが、以下の関連機能があり今後サポートされることが期待されます。

関連機能はサポート時期が新しいものが多く、場合によってはdialog要素を使用しづらいかもしれません。ですが、dialog要素の登場以前に必要だった複雑な制御を考えると、dialog要素を使用するメリットは十分にあると思います。

dialog要素の採用が難しい場合は、まずは独自の実装は避けてUIライブラリの利用を検討するとよいでしょう。UIライブラリの特徴や選び方については、記事『ウェブ制作にも便利!React & Vueで始めるヘッドレスUI』も参考にしてみてください。

参考記事

SNSでシェアしよう
シェアいただくと、サイト運営の励みになります!
X(旧Twitter)へポスト
はてなブックマークへ投稿
URLをコピー
古舘 和志

フロントエンドエンジニア。ウェブデザイナーのようなHTMLコーダーからフロントエンドエンジニアに転身。現在は岩手からリモートで勤務中。

この担当の記事一覧