モーダルをdialog要素で実装してみる

主要ブラウザがdialog要素が対応してそろそろ1年経つので、そろそろ良いだろうと実際の制作で使用してみました。

dialog要素を使用することによって得られるメリットと、実装にあたって引っかかった点などをまとめておきます。

dialog要素を導入するメリット

アクセシビリティ対応が楽になる

個人的に最大の恩恵はアクセシビリティ対応が簡単になったことだと思っています。

従来のモーダル使用には、

  1. モーダルをひらくボタンを押す(モーダルが表示される)
  2. モーダルがひらかれたことを通知
  3. フォーカスをモーダルにうつす
  4. (背面コンテンツにフォーカスが移ってしまわないようにする)
  5. モーダルをとじるボタンを押す(モーダルが非表示になる)
  6. モーダルが閉じられたことを通知
  7. フォーカスを元のボタンにもどす

このように基本的な点だけでも様々配慮が必要だったのですが、dialog要素(dialog.api)がこの辺りをまるごと対応してくれます。

また、モーダルが開いていているときにEscキーを押すことでもcancelイベントが発生しモーダルが閉じられます。

キーボードユーザーには非常に助かります。

ただ、自分の環境(Windows & NVDA | iPhone & Voice Over)では、モーダルを開いた、閉じたは通知してくれませんでした(唐突にフォーカス対象の読み上げを行なう)。

この点はもう少し調べてみたいと思います。

少ないコードで実装できる

というわけで、コード量も少なくて済みます。大切なメリットです。

基本のdialog要素実装

HTML側はシンプルにdialog要素で囲むだけです。

<dialog>
  <div>I'm modal</div>
</dialog>

dialog要素は他コンテンツから独立した要素として扱われ標準で非表示となります。ただしバージョンが古いブラウザや非対応ブラウザでは普通に表示されてしまいますので、非対応ブラウザにも対応するのであれば別途CSSが必要です。

dialogを開く

JSからshow()、showModal()を実行するとモーダル表示状態となり、dialog要素にはopen属性が付与されます。

<dialog open="">
   <div>表示されているときはopen属性が付与されています</div>
</dialog>

show()とshowModal()のちがい

show()、showModal()はどちらもモーダルをひらきますが、

show()はモーダルを開きながら他要素の操作も可能、

showModal()ではモーダル以外の要素が操作不可

という違いがあります。

dialogをとじる

先述のとおり、Escキー押下でも閉じることができますが、それだけだとスマートフォンやスクリーンリーダーユーザーが対応できないので閉じるボタンは必須です。

JSでの操作はシンプルで、dialog.close() でモーダルを閉じられます。

Escキーで閉じた場合はcancelイベント、close()の場合はcloseイベントという扱いになります。

dialog要素に関するCSS

dialog要素には適切な表示となるようブラウザCSSが色々働きかけます。

ブラウザごとに多少の違いはあれど、dialog要素自身にはmax-width、max-heightが指定され、dialogのコンテンツサイズはfit-contentで中央表示されます。

たとえばドロワーメニューをdialogで実装する場合など、自前のCSSで上書きしなければ真ん中にどんと表示されます。

showModalの背景は::backdropで変更できる

showModal()されたdialogには透過された背景色が適用されますが、dialog::backdrop{ style }で背景を変更することができます。

::backdropにbackground-imageを指定すれば、好きな画像やグラデーションをあてることも可能です。

formをmethod=”dialog”で使用する場合

dialog要素の中にformを置き、method=”dialog”指定すると、formの送信ボタンを押してもデータ送信は行われずモーダルが閉じます。

この場合、dialog.close()の引数に取り出したい値をセットすることで取り出すことができます。分かりにくいので作ったデモが以下です。

See the Pen form method dialog by Takashi Abe (@TakeshiAbe) on CodePen.

デモを見たくない人用に書いておくと、

  1. dialog.close( value ) でdialogを閉じる。
  2. dialogのcloseイベントにリスナーを登録しておく。dialog.returnValueで値を取得できる。

という仕組みです。

背面コンテンツのスクロール抑止は別途必要

dialog要素のモーダルは背面スクロールの問題には対応してくれません。

背面スクロールを防ぐ方法は色々ありますが、overscroll-behaviorプロパティによる抑止が一番シンプルで良いと思います。

overscroll-behaviorの使い方については別記事にまとめます。

タイトルとURLをコピーしました