ダイアログ作りのヒント

この記事は、トラストバンク Advent Calendar 2022の5日目です。

フロントエンドエンジニアの片柳です。

新米フロントエンドエンジニアから、ワンステップ踏み出すための機能のひとつに、ダイアログ(通称モーダル)があります。
シンプルなUIゆえに様々なサイトに多用されますが、いざ実装するとかなり複雑な要件を含んでいることがわかります。
明日からダイアログを作るフロントエンドエンジニアの皆さんが、少しでも簡単に実装するためのヒントを、私なりに書いてみようと思います。

ダイアログ VS モーダル

ダイアログ(Dialog)とは、対話のことです。

ダイアログは、ユーザーの行動をトリガーとして、画面上に何かしらのコンテンツを表示させることで対話を行おうとします。
ユーザーはそれに対して答えることができ、また必要に応じて無視することもできます。

はてなブログの下書きプレビューダイアログ。表示されていても、画面をスクロールして閲覧することができる。

ユーザーの行動をトリガーとせず、唐突に表示されるダイアログの総称がポップアップ(Popup)です。
無視されるダイアログの例として、まずポップアップをイメージされる方が多いのではないでしょうか。

対してモーダル(Modal)は、ユーザーが無視することができない対話を提供します。
入力をする・クリックする・送信するなど、何かしらの対話を行わないと、ユーザーは元の処理に戻ることができません。

ふるさとチョイスの検索モーダル。「絞り込み」を押下しないと画面に戻ることができない。

つまり、モーダルもダイアログの一種であり、ユーザーにどの程度の制約を与えるかによって呼び方が変わるということです。

具体的な選択肢

ダイアログの機能を実現するための選択肢は、大きく分けて3つです。

window.alertwindow.confirm

alert('データが読み込めませんでした');
confirm('申し込みを確定しますか?');

画面上にテキストを表示し、ユーザーがボタンを押すまで行動を制限するこの機能は「モーダル」にあたります。
どんなブラウザにも対応しており、仕様も明快なので、簡単に実装できることがメリットです。

ただし、デザインの調整は不可能であり、表示できるコンテンツもテキストのみと限られています。
かなりシステマチックな印象を受けるため、「何かしらのエラーが発生したのでは」と捉えるユーザーも多いのではないでしょうか。

<dialog>

<dialog id="dialog">こんなお礼の品はいかがでしょうか?</dialog>
const dialog = document.getElementById('dialog');

dialog.showModal();

2021年以降、新たに導入されたHTML要素であり、現時点ではIEを除くほとんどのブラウザで利用できます。
JavaScript側から制御を与えることで、ダイアログとモーダル双方の機能を同じ要素で両立することができ、キーボードやフォーカスの操作についても自動で行なってくれます。

唯一のデメリットは、モーダルの表示ステートを外部で維持することができないという点です。
Reactなど、DOMを定期的にレンダリングし直すようなライブラリを導入している場合、レンダリング後もモーダルを表示しておきたいという要件が発生しがちです。
<dialog>のモーダル表示はshowModal()を実行した時のみ表示されるため、モーダル表示前後のイベントに不具合が出る可能性があります。

role="dialog"

<div id="dialog" role="dialog" aria-modal="true">こんなお礼の品はいかがでしょうか?</div>

実装上、最も自由度が高いのは、div要素に対してrolearia-modal付与し、アクセシビリティツリー上はダイアログだということにしてしまうことです。
あらゆる環境で利用・実装できますが、ダイアログを成り立たせるために必要なの機能を全て自分で実装しなくてはいけないことが最大のデメリットです。

例えば、ボタンを押してダイアログを開いた時、キーボードフォーカスがボタンに残したままにしてしまうと、ユーザーはダイアログ内のコンテンツにたどり着くことができません。
Tabキーを用いたフォーカス移動も適宜監視し、最初のdiv要素の外に出て行かないように制御する必要があります。
また、初期実装で作られたすべての機能をメンテナンスしつつ維持しなければいけない点も課題です。

では、何を使うべきか

実装 カスタマイズ性 汎用性 運用
window.alert
window.confirm
低い ほぼ不可能 どんな環境でも利用可能 容易
<dialog> 普通 可能 一部のブラウザでは利用不可
DOMレンダリングが発生するが発生する環境ではバグを発生させる可能性がある
容易
role="dialog" 難しい あらゆる形にカスタマイズ可能 どんな環境でも利用可能
定期的なメンテナンスとリファクタリングが必要
難しい

ネイティブのJavaScriptを使う環境ならば、<dialog>をおすすめします。
ダイアログとして最低限ほしい機能が全て入っており、実装者が特別アクセシビリティに対して知識を持っていなくても、セマンティックなダイアログを実装することができます。

すべてのブラウザが<dialog>に対応しているわけではない現状を考えると、role="dialog"を用いた実装ももうしばらくは利用していくことになりそうです。
ただ、<dialog>が誕生したことにより、今まで開発者が各々で考えていたフォーカスイベントやアクセシビリティツリーのお手本ができました。これらをうまく活用しつつ、オリジナルのダイアログを作り続けていくのも楽しいかもしれませんね。

最後に

トラストバンクでは一緒に活躍するエンジニアを募集中です。 www.wantedly.com

明日は@tb_obaさんが書いてくれます。お楽しみに。