HTMLのdialog要素の使い方をしっかり理解モーダルを作成してみよう
HTMLのdialog要素(ダイアログ要素)を利用することで 最小限のJavaScriptでモーダルウィンドウを作成することが可能です。dialog 要素をこれまで使った経験がない人であれば dialog 要素を利用することでこんなことができるのかと驚きがあるかもしれません。本文書は dialog 要素の基本的な機能と使い方を確認してシンプルなコードでモーダルウィンドウを作成していきます。読み終えるとdialog要素の基本はしっかりと理解することができます。
目次
Dialog の設定
dialog要素はHTMLの要素なのでなにか特別な環境を構築する必要はなくindex.htmlファイルで動作確認を行うことができます。
はじめての Dialog タグ
dialog要素のみでどのようなことができるのか確認するために任意の場所にindex.htmlファイルを作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dialog element</title>
</head>
<body>
<dialog>
<h2>Dialog</h2>
</dialog>
</body>
</html>
dialog 要素の中に h2 タグで Dialog という文字列を設定していますが index.html ファイルをブラウザで開いても画面上には何も表示されません。
dialog 要素に open 属性を追加します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dialog element</title>
</head>
<body>
<dialog open>
<h2>Dialog</h2>
</dialog>
</body>
</html>
dialog 要素に open 属性を追加することで、border で囲まれた ”Dialog” という文字列が表示されます。
dialog 要素に open 属性をつけない場合は何も表示されず、open 属性を追加すると dialog 要素の中に設定したコンテンツが表示されることが確認できました。
これだけの機能であれば何もメリットはありませんが、open属性を追加するかしないかで表示・非表示を切り替えることができることがモーダルウィンドウをdialog要素で作成するためのポイントとなります。ここからはJavaScript を追加してモーダルウィンドウを作成するために必要な機能を追加していきます。
JavaScript の追加
show メソッド
dialog 要素の外側に Dialog を表示する際にクリックする”Open”ボタンを追加して dialog 要素の内側に非表示にするための”Close”ボタンを追加します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dialog element</title>
</head>
<body>
<button class="openBtn">Open</button>
<dialog>
<h2>Dialog</h2>
<button class="closeBtn">Close</button>
</dialog>
</body>
</html>
ボタンを追加しただけなのでボタンをクリックしても何も変化はありません。
JavaScript の document.querySelector を利用して dialog 要素と button 要素を取得します。取得したbutton 要素にクリックイベントを設定し、ボタンをクリックすると dialog 要素が持っている show メソッドを実行します。dialog要素はメソッドも持っています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dialog element</title>
</head>
<body>
<button class="openBtn">Open</button>
<dialog>
<h2>Dialog</h2>
<button class="closeBtn">Close</button>
</dialog>
<script>
const dialog = document.querySelector('dialog');
const openBtn = document.querySelector('.openBtn');
openBtn.addEventListener('click', () => {
dialog.show();
});
</script>
</body>
</html>
“Open”ボタンをクリックするとブラウザ上に Dialog が表示されるようになりました。
デベロッパーツールで要素を確認すると show メソッドを実行することで dialog 要素に open 属性が追加されていることがわかります。dialog が非表示の時は CSS の display プロパティは”none”に設定されていますがボタンをクリックすると以下の CSS のスタイルの設定により Dialog が表示されるようになります。つまり display プロパティの”none”と”block”によって非表示から表示に変わっていることがわかります。
dialog[open] {
display: block;
}
show メソッドでは open 属性が dialog 要素に追加されますが show メソッドを利用せず JavaScript によって open 属性を追加することでも Dialog が表示されます。下記では setAttribute メソッドで属性 open を dialog 要素に追加しています。
openBtn.addEventListener('click', () => {
dialog.setAttribute('open', '');
});
showModal メソッド
show メソッドの代わりに今度は showModal メソッドを利用します。showModal メソッドも dialog 要素が持っているメソッドです。showModal を実行すると名前に Modal がついているようにモーダルウィンドウのように背景が追加されます。
<script>
const dialog = document.querySelector('dialog');
const openBtn = document.querySelector('.openBtn');
openBtn.addEventListener('click', () => {
dialog.showModal();
});
</script>
ブラウザ上で動作確認を行うと以下のように表示されます。
ブラウザのデベロッパーツールを利用して要素の確認を行います。top-layer と backdrop という文字列を確認することができます。要素の重なりを設定する際に z-index を利用して要素の重なりを設定することができますが top-layer は z-index を設定しなくても一番上の Layer として表示されるので Dialog が表示された場合に下の Layer にアクセスすることができせん。ここでは”Open”ボタンは表示されている Dialog の下のレイヤーになるためアクセスすることはできません。backdrop は背景色を設定するために利用することができます。デフォルトでは薄いグレーが設定されています。
show メソッドを利用した場合は top-layer は設定されないので Dialog が表示されても”open”ボタンをクリックすることができます。確認した通り show メソッドでは backdrop が設定されることもありません。open 属性を設定した場合も同様です。
Escape キー
showModal で Dialog を表示した状態で Escape キーを押してしてください。Escape キーを押すと表示されていた Dialog が非表示になります。
close メソッド
Dialog を非表示にしたい場合には close メソッドを利用することができます。close メソッドを実行すると表示されていた Dialog は非表示となります。
<script>
const dialog = document.querySelector('dialog');
const openBtn = document.querySelector('.openBtn');
const closeBtn = document.querySelector('.closeBtn');
openBtn.addEventListener('click', () => {
dialog.showModal();
});
closeBtn.addEventListener('click', () => {
dialog.close();
});
</script>
showModal と close メソッドを利用することで Dialog の表示/非表示を切り替えれるようになりました。
form タグの利用
close メソッドを利用して Dialog を非表示にすることができましたが form 要素を利用することで Dialog を非表示にすることができます。form 要素を利用する場合には method 属性に”dialog”を設定し button 要素を追加します。form 要素は dialog 要素の中に追加します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dialog element</title>
</head>
<body>
<button class="openBtn">Open</button>
<dialog>
<h2>Dialog</h2>
<button class="closeBtn">Close</button>
<form method="dialog">
<button>submit</button>
</form>
</dialog>
<script>
const dialog = document.querySelector('dialog');
const openBtn = document.querySelector('.openBtn');
const closeBtn = document.querySelector('.closeBtn');
openBtn.addEventListener('click', () => {
dialog.showModal();
});
closeBtn.addEventListener('click', () => {
dialog.close();
});
</script>
</body>
</html>
form 要素を設定後は close メソッドを利用しなくても”submit”ボタンをクリックすることで Dialog を非表示にすることができるようになりました。method 属性の値を”dialog”から変更または削除すると動作しページの再読み込みが行われます。Dialog は非表示になりますが非表示から表示になったのではなくページの再読み込みによって元の状態に戻っただけです。
form 要素の method 属性ではなく button 要素の formmethod 属性に”dialog”を設定しても Dialog を非表示にすることができます。formmethod を利用するとデフォルトの動作であるページの再読み込みも行われず method に”post”を設定している場合でも POST リクエストが送信されません。form を実行させたいことから formmethod 属性は cancel ボタンなどに利用することができます。
<!DOCTYPE html'gt;
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dialog element</title>
</head>
<body>
<button class="openBtn">Open</button>
<dialog>
<h2>Dialog</h2>
<button class="closeBtn">Close</button>
<form method="post">
<button formmethod="dialog">cancel</button>
<button>submit</button>
</form>
</dialog>
<script>
const dialog = document.querySelector('dialog');
const openBtn = document.querySelector('.openBtn');
const closeBtn = document.querySelector('.closeBtn');
openBtn.addEventListener('click', () => {
dialog.showModal();
});
closeBtn.addEventListener('click', () => {
dialog.close();
});
</script>
</body>
</html>
“cancel”ボタンを追加
ここまでの動作確認で Dailog を非表示にする際には”Escape キー”, “dialog要素が持つclose メソッド”, “form 要素”を利用して行えることがわかりました。
close イベント
Dialog を非表示にする場合に close イベントを設定することで Dialog が非表示になったことを検知することができます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dialog element</title>
</head>
<body>
<button class="openBtn">Open</button>
<dialog>
<h2>Dialog</h2>
<button class="closeBtn">Close</button>
<form method="dialog">
<button>cancel</button>
<button>submit</button>
</form>
</dialog>
<script>
const dialog = document.querySelector('dialog');
const openBtn = document.querySelector('.openBtn');
const closeBtn = document.querySelector('.closeBtn');
openBtn.addEventListener('click', () => {
dialog.showModal();
});
closeBtn.addEventListener('click', () => {
dialog.close();
});
dialog.addEventListener('close', (e) => {
console.log('close');
});
</script>
</body>
</html>
どのボタンをクリックしてもコンソールには close イベントによって”close”の文字列が表示されます。“Escape キー”を押した場合にも close イベントが発火されます。
returnValue
form 要素内の Button 要素に value を設定することでクリックしたボタンの value の値を取得することができます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dialog element</title>
</head>
<body>
<button class="openBtn">Open</button>
<dialog>
<h2>Dialog</h2>
<button class="closeBtn" value="close">Close</button>
<form method="dialog">
<button value="cancel">cancel</button>
<button value="submit">submit</button>
</form>
</dialog>
<script>
const dialog = document.querySelector('dialog');
const openBtn = document.querySelector('.openBtn');
const closeBtn = document.querySelector('.closeBtn');
openBtn.addEventListener('click', () => {
dialog.showModal();
});
closeBtn.addEventListener('click', () => {
dialog.close();
});
dialog.addEventListener('close', () => {
console.log('close')
console.log('returnValue:', dialog.returnValue);
});
</script>
</body>
</html>
form 要素の中のボタンを押すとコンソールには設定した value が表示されます。“cancel”ボタンであれば”cancel”、“submit”ボタンであれば”submit”とコンソールに表示されます。しかし form 要素の外側にある”close”ボタンを押した場合には”空白”または”close”ボタンを押す前に form 要素の中のボタンをクリックしている場合はクリックしたボタンの value が表示されます。dialog 要素自体が returnValue を保存しているので form 要素内のボタンを押すと returnValue が更新されますが外側の”close”ボタンをクリックすると保存している値が更新されないためです。
returnValueの値を利用することでクリックしたボタンによって異なる処理を行うといったことも可能になります。
Dialog の外側のクリック
Dialog の外側にある背景をクリックすることで Dialog を非表示にするための設定を確認していきます。モーダルウィンドウでは背景をオーバーレイと呼ぶことがありますがオーバーレイをクリックした場合に表示されている Dialog を非表示にします。
dialog 要素の中に div 要素を追加することで追加した div 要素内であればクリックしたとしても Dialog を非表示にさせないように設定を行います。style を設定することで dialog の領域一杯にクリック領域を調整しています。
<dialog style="padding: 0">
<div style="padding: 1em">
<h2>Dialog</h2>
<button class="closeBtn" value="close">Close</button>
<form>
<button value="cancel">cancel</button>
<button value="submit">submit</button>
</form>
</div>
</dialog>
dialog 要素に対して click イベントを設定してクリックした要素(event.target)が dialog 要素の場合のみ close メソッドを実行します。実際にクリックした時にevent.targetに含まれる情報を確認できるようにconsole.log(event.target)を設定しています。
dialog.addEventListener('click', (event) => {
console.log(event.target);
if (event.target === dialog) {
dialog.close();
}
});
dialog 要素の内側をクリックした場合にはコンソールには下記のように dialog 要素の中のdiv要素の情報が表示されます。
dialog 要素の外側をクリックした場合にはコンソールには下記のように dialog 要素の情報が表示されます。この時にevent.targetがdialogと一致してdialog.closeメソッドが実行されます。
クリックしても正しく非表示にならない場合はevent.targetに含まれる情報で確認を行ってください。
背景色の変更
背景色を変更したい場合や Dialog の border を消したい場合には style で設定を行うだけです。dialog に border が設定されているので”border:0”でボーダーは消えます。背景は backdrop で設定されているので backdrop に背景色を設定することで指定した色に変わります。
<style>
dialog {
border: 0;
}
dialog::backdrop {
background-color: skyblue;
}
</style>
アニメーションの設定
表示から非表示に非表示から表示に変わる際にアニメーションを設定する方法はいくつかありますがここでは opacity を利用してアニメーション設定を行います。
説明した通り dialog 要素は非表示から表示の切り替えは display の値を”none”から”block”にすることで実現しています。
styleを利用して非表示の状態ではdisplayを”block”、 opacityを0として非表示になるようにします。
<style>
dialog {
border: 0;
display: block;
}
dialog:not([open]) {
opacity: 0;
}
dialog::backdrop {
background-color: skyblue;
}
</style>
設定しても動作に変わりはありませんがopacityは0で要素は存在はしているがブラウザ上には見えない状態となっています。display が”none”の場合は DOM に dialog 要素は存在しません。
transition を利用してアニメーションの設定を行います。
<style>
dialog {
border: 0;
display: block;
transition: opacity 0.5s ease-out;
}
dialog:not([open]) {
opacity: 0;
}
dialog::backdrop {
background-color: skyblue;
}
</style>
transition を追加することで”Open”ボタンをクリックするとゆっくりと Dialog が表示されます。しかし Dialog を非表示にするためボタンか Dialog の外側をクリックすると 画面中央で非表示になるのではなく display の値を”block”にして opacity を設定していない時の位置に Dialog が一瞬表示されて消えます。
Dialogが一瞬表示される位置はdisplay の値を”block”にしてopacityを設定していない設定を行うことで確認することができます。Dialogがずっと表示されている状態です。
<style>
dialog {
border: 0;
display: block;
}
dialog::backdrop {
background-color: skyblue;
}
</style>
style を利用して Dialog の表示場所を設定します。position は fixed, inset は 0 にしています。
<style>
dialog {
border: 0;
display: block;
position: fixed;
inset: 0;
transition: opacity 0.5s ease-out;
}
dialog:not([open]) {
opacity: 0;
}
dialog::backdrop {
background-color: skyblue;
}
</style>
position を設定後は Dialog が表示された場所と同じ場所の画面中央で非表示になるためゆっくりと Dialog が表示され、ゆっくりと非表示になります。
背景のスクロールの停止
ページ内のコンテンツが多くスクロールが可能なページで Dialog を表示すると Dialog が中央に表示されていても背景のスクロールを行うことができます。Dialog が表示された状態で背景のスクロールを停止するために以下の style を追加します。
html:has(dialog[open]) {
overflow: hidden;
}
Dialog が表示されている場合は overflow の設定が有効になり背景のスクロールを止めることができます。
ここまで読み進めた人であれば dialog 要素の理解が深まり、dialog 要素を利用したモーダルウィンドウの作成も理解できたのではないでしょうか。
最終的なコードは下記の通りです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dialog element</title>
<style>
dialog {
border: 0;
display: block;
position: fixed;
inset: 0;
transition: opacity 0.5s ease-out;
}
dialog:not([open]) {
opacity: 0;
}
dialog::backdrop {
background-color: skyblue;
}
html:has(dialog[open]) {
overflow: hidden;
}
</style>
</head>
<body>
<button class="openBtn">Open</button>
<dialog style="padding: 0">
<div style="padding: 1em">
<h2>Dialog</h2>
<button class="closeBtn" value="close">Close</button>
<form>
<button value="cancel">cancel</button>
<button value="submit">submit</button>
</form>
</div>
</dialog>
<script>
const dialog = document.querySelector('dialog');
const openBtn = document.querySelector('.openBtn');
const closeBtn = document.querySelector('.closeBtn');
openBtn.addEventListener('click', () => {
dialog.showModal();
});
closeBtn.addEventListener('click', () => {
dialog.close();
});
dialog.addEventListener('close', () => {
console.log('returnValue:', dialog.returnValue);
});
dialog.addEventListener('click', (event) => {
console.log(event.target);
if (event.target === dialog) {
dialog.close();
}
});
</script>
</body>
</html>