JavaScriptなしでモーダルが開く。HTMLのcommand属性でできること

モーダルを開くボタンを書くとき、「ボタンをクリックしたらshowModal()を呼ぶ」というJavaScriptを用意するのが定番でした。

今は、<button>commandcommandforを使って、ダイアログやポップオーバーの基本操作をHTMLだけで宣言できます。Invoker Commands APIは2025年12月からBaseline Newly availableです。

JavaScriptが全部不要になる機能ではありませんが、「このボタンはこの要素を開く・閉じる」という関係をHTMLに直接書けるのがポイントです。

commandforで相手を指定し、commandで操作を書く

基本は2つです。

  • commandfor:操作する要素のidを指定する
  • command:その要素へ行う操作を指定する

たとえば、<dialog>をモーダルとして開くなら次のように書けます。

<button commandfor="my-dialog" command="show-modal">
  詳細を見る
</button>

<dialog id="my-dialog">
  <p>ダイアログの内容</p>
  <button commandfor="my-dialog" command="close">閉じる</button>
</dialog>

show-modaldialog.showModal()closedialog.close()に相当する操作です。ボタンと対象の関係がHTMLを見ただけで追いやすくなります。

Chromeでは135から使えます。古いブラウザまで対象にする場合は、実際の利用環境を確認してください。

dialogではshow-modal、close、request-closeを使える

ダイアログ向けの主なコマンドは3つです。

command動き
show-modalダイアログをモーダルとして開く
closeダイアログを閉じる
request-close閉じる要求を出し、cancelイベントを経て閉じる

request-closeは単純なcloseと少し違います。cancelイベントを発生させるため、必要ならJavaScript側でpreventDefault()して閉じる処理を止められます。

「入力途中なら確認を出したい」といった拡張余地を残すなら、request-closeを選ぶ理由があります。逆に、ただ閉じればよいボタンならcloseで十分です。

ポップオーバーも同じ書き方で操作できる

popover属性を付けた要素には、次の3つのコマンドがあります。

  • show-popover
  • hide-popover
  • toggle-popover
<button commandfor="help" command="toggle-popover">
  ヘルプ
</button>

<div id="help" popover>
  入力方法の補足です。
</div>

Popover APIには、以前からpopovertargetpopovertargetactionがあります。ポップオーバーだけを操作するなら、そちらも現在の正規の書き方です。

commandcommandforは対象がポップオーバーに限られず、dialogや独自コマンドまで同じ仕組みで扱えるのが違いです。既存コードを無理に全部置き換えるより、新しく作るUIで操作方法をそろえたいときに検討しやすいでしょう。

独自コマンドは–から始め、JavaScriptで受け取る

用意されている操作だけで足りない場合は、--で始まる独自コマンド名を指定できます。

<button commandfor="profile-card" command="--toggle-favorite">
  お気に入りを切り替える
</button>

<div id="profile-card">...</div>

対象要素ではcommandイベントを受け取ります。

const card = document.querySelector('#profile-card');

card.addEventListener('command', (event) => {
  if (event.command === '--toggle-favorite') {
    card.classList.toggle('is-favorite');
  }
});

ここではJavaScriptを使います。それでも、「どのボタンがどの要素へ何を要求するか」はHTML側に残せます。

対応状況は、属性と個別のコマンドを分けて見る

ここで一つ注意があります。command属性そのものと、個々のコマンドでは、対応が始まった時期が違います。

2026年9月15日に確認した主要デスクトップブラウザの対応開始は次のとおりです。iOS版のSafariも、同じバージョン系列で対応しています。

機能Chrome・EdgeFirefoxSafari
command・commandfor、基本の開閉コマンド135以降144以降26.2以降
request-closeコマンド139以降144以降26.2以降

未対応のブラウザでは、command属性を付けても開閉は動きません。必要な情報をダイアログの中だけに置くなら、見た目を足す機能より慎重に対応範囲を決めたいところです。

既存のJavaScript実装を置き換えるときも、一度に削るより、対象ブラウザと操作の範囲を先に確認します。クリック処理とcommandの両方を無条件に残すと、特にトグル操作では二重に実行される原因になります。

onclickよりも、操作の意味をHTMLへ寄せられる

onclick="myDialog.showModal()"でも短くは書けます。違いは、commandがHTMLの仕組みとして対象と操作を表すことです。

Chromeの解説では、組み込みコマンドを使うことでフォーカス管理やアクセシビリティ上の振る舞いをブラウザ側へ任せやすくなる点も挙げられています。独自にイベント処理を組み立てる量を減らせるぶん、標準の要素が持つ挙動をそのまま活かせます。

ただし、command属性にしただけでダイアログのアクセシビリティが別物になるわけではありません。onclickからshowModal()を呼ぶ場合も、開いたあとは同じ<dialog>です。減らせるのは、ボタンから対象を探して標準の操作を呼ぶためのコードで、見出し、閉じる手段、初期フォーカス、長文のスクロールは別に確認します。

とはいえ、フォームの保存確認やアプリ固有の状態管理まで自動化されるわけではありません。ダイアログを開く・閉じる、ポップオーバーを表示する、といった標準操作から置き換えるのが分かりやすい使い方です。

開いたときの見た目をふわっと変えたいなら、@starting-styleでフェードさせる方法も合わせて読んでみてください。開閉はHTML、見せ方はCSSという分け方にできます!

SNS SHARE

はてなブックマークでシェアXでシェアLINEでシェアPocketでシェア