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

27

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

モーダルとは、表示している間は他の操作をできなくするUIを指します。dialog要素の登場以前は、次のような制御を自前で用意する必要がありました。

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

こうした複雑さについては記事『HTMLでモーダルUIを作るときに気をつけたいこと』でも解説しています。苦労してきたフロントエンドエンジニアは多いでしょう。

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

本記事で紹介すること:

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

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()などの開閉用の関数
  • 表示されている間はほかの操作を制限し、閉じたら制限を解除

dialog要素を利用することで、最小限のHTMLと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()メソッドを実行
  • JavaScriptでrequestClose()メソッドを実行
  • 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:ダイアログを閉じる要求(ESC や戻る操作、requestClose()など)が発生したときに発火。event.preventDefault()で閉じるのを抑止可能。

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プロパティは空文字("")を返します。

コラム:close()とrequestClose()メソッドの違い

dialog要素のrequestClose()メソッドは、「cancelイベントを先に発火し、リスナーで抑止できる」閉じ方であり、close()メソッドとは性質が異なります。

たとえば、入力フォームを含むモーダルがあるとしましょう。モーダルを閉じる前に、REST APIをコールし、成功した場合にのみモーダルを閉じるといった使い方ができます(=失敗した場合はモーダルを閉じず、入力フォームを維持したい場合)。

またrequestClose()close()と同様に引数でreturnValueを設定できます。

requestClose()メソッドは、Chrome 134・Edge 134(2025年3月)、Safari 18.4(2025年3月)、Firefox 139(2025年5月)以降で利用可能です。

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

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

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

command属性・commandfor属性

ここまでの説明では、JavaScriptを使っていました。しかし、dialog要素のcommandcommandfor属性を使うとJavaScriptを利用せずにモーダルの開閉ができます

使い方は簡単で、次の3つの手順となります。

  1. dialog要素にid属性を指定。
  2. commandfor属性は操作したいdialog要素のid値を指定。
  3. command属性で命令を記載(show-modalclose等)。
<button command="show-modal" commandfor="my-dialog">モーダルダイアログをひらく</button>

<dialog id="my-dialog">
  <p>モーダルダイアログの説明文が入ります。</p>
  <button command="close" commandfor="my-dialog">とじる</button>
</dialog>

command属性には以下の値を指定します。いずれもJavaScriptの命令と同じものが使えます。

  • show-modal:モーダルとして開く(JSのshowModal()と同じ役割)
  • close:閉じる(JSのclose()と同じ役割)
  • request-close:閉じる要求を行う(JSのrequestClose()と同じ役割)

対応ブラウザ

command属性は、Chrome 135・Edge 135(2025年4月)、Safari 26.2(2025年12月)、Firefox 144(2025年10月)以降で利用可能です。

closedby属性

dialog要素の便利なHTML属性として、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属性は、Chrome 79・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で指定。

さらに応用すると次のようなデモもできます。アンカーポジショニングlinear()関数を使っています。

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

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

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

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

まとめ

モーダルUIを任意の<div>タグ等で自前実装しようとすると複雑な制御が必要になります。世の中には本来モーダルとして対処すべき点を未実装のまま公開しているウェブサイトも多々あります。

dialog要素を利用すれば、難しい制御も要りません。シンプルに確実にモーダルダイアログを実装できるので、まずはdialog要素を使ってモーダルを実装してみてはいかがでしょうか?

また、関連機能があり今後サポートされることが期待されます。

  • CloseWatcher API:ブラウザやOSの閉じる動作を制御する。ESCキーが押下されたらdialog要素を本当に閉じるかどうかを確認できる
SNSでシェアしよう
シェアいただくと、サイト運営の励みになります!
X(旧Twitter)へポスト
はてなブックマークへ投稿
URLをコピー
編集部

ICS MEDIAは株式会社ICSが運営するオウンドメディアです。ICSはインタラクションデザイン専門のプロダクション。最先端のウェブテクノロジーを駆使し、オンスクリーンメディアの表現分野で活動しています。

この担当の記事一覧