🎈
2024 年の Baseline に追加された機能として Popover API があり、またそれに関連するものとして Dialog がすでに実装されている。 この 2 つは目的は違うが技術的には似たものになっているので、それぞれの特徴などをまとめる。
動作サンプル: https://yaakaito.github.io/code-sandbox/popover-and-dialog/
従来のz-indexとは異なる、ブラウザネイティブの最前面レイヤー。PopoverとDialogのモーダルモードは、このTop layerを利用して要素を描画する。入れ子も可能だが、Light Dismiss(要素外クリックでの閉じる)の挙動には注意が必要となる。
Popover は属性で、任意の要素を Top layer へ配置することができる。ダイアログやモーダルではないので、アクセシビリティといった視点で意味は持たず、基本的には背景の要素に操作が貫通する。
<button popovertarget="mypopover">toggle popover</button>
<div id="mypopover" popover="auto|manual|hint">Popover content</div>
popover 属性には現状 3 つの値を設定することができ、これによって Light Dismiss や表示方法をコントロールする。
auto
: デフォルト値で、Light Dismiss が有効になるmanual
: Light Dismiss が無効になり、閉じるための動作を実装する必要があるhint
: 「hint」は Chrome 133 から導入された(される予定の)機能で、この値が設定されている Popover は同時に1つしか表示されないJS からもコントロール可能だが、必ず属性に popover
が付与されている必要がある。
popover.showPopover()
popover.hidePopover()
<dialog>: ダイアログ要素 - HTML: ハイパーテキストマークアップ言語 | MDN
Dialog はユーザーに操作を求めるための役割(role=dialog
)を持つ。モーダル/非モーダルの2つのモードがあり、特にモーダルモードではTop layerへの表示と背景操作のロックを行う。JS での制御が想定されていて、showModal()
を利用することでモーダルダイアログが、show()
を利用することで非モーダルダイアログが表示される。
<dialog id="dialog">
<h1>Dialog</h1>
<p>This is a dialog.</p>
<button>Close</button>
</dialog>
<script>
const dialog = document.getElementById('dialog');
dialog.showModal();
</script>
モーダルの場合、次の機能がブラウザネイティブで提供される。これによって、これまでの実装よりもアクセシビリティや UX の向上が期待できる。
.showModal()
で表示された Dialog は、Top Layer でない要素が inert となるCSS アンカー位置指定の使用 - CSS: カスケーディングスタイルシート | MDN
特定要素を基準とした配置をサポート。Top layer要素でも有効で、Popoverと組み合わせて使用できる。
<style>
#anchorPopoverTrigger {
anchor-name: --anchor-target;
}
#anchorPopover {
position-anchor: --anchor-target;
position-area: bottom span-right;
position-try-fallbacks: flip-inline, flip-inline flip-block;
margin: 0;
width: 300px;
}
</style>
<button id="anchorPopoverTrigger" popovertarget="anchorPopoverLeft">toggle anchor popover</button>
<div id="anchorPopover" popover>
Anchor Popover Left
</div>
position-area による配置指定が基本となりそうだが、top や left などを使用した直接的な位置指定も可能。
fallback を設定することができ、画面に収まらない場合の挙動を調整することが出来る。
ユーザーが閉じようとした動作を検知できる API で、具体的には ESC や戻るを押したタイミングが検知できる。popover=manual
や非モーダルダイアログには ESC での閉じる機能がないので、対応する必要が出てきたときに有効。
let closeWatcher = null
popoverTrigger.addEventListener('click', () => {
if (closeWatcher) {
closeWatcher.destroy()
}
closeWatcher = new CloseWatcher()
closeWatcher.onclose = () => {
popover.hidePopover()
}
popover.showPopover()
})