ToggleEvent.sourceでポップオーバーを開いた要素を特定する

ToggleEvent.sourceはBaseline 2026で追加された、toggle / beforetoggleイベントに発火元の要素を載せて渡すプロパティです。同じポップオーバーを複数のボタンから開くケースで、どのボタンから開かれたかをリスナー側で素直に判定できます。

公開: 2026年5月23日(土)
更新: 2026年5月23日(土)

はじめに

Popover APIや<details><dialog>では、開閉のたびにbeforetoggle / toggleイベントが発火します。複数のトリガーから1つのポップオーバーを開くようなUIでは、リスナーの中で今回はどのボタンから開かれたのかを知りたい場面が出てきます。

これまでは、各ボタンに別々のイベントを張って状態を別管理する、データ属性を見て分岐する、といった工夫が必要でした。Baseline 2026で追加されたToggleEvent.sourceは、その発火元をToggleEvent自体に載せて渡してくれるプロパティです。

どんなプロパティか

toggle / beforetoggleイベントはToggleEventインターフェースを持ちます。これまではnewState / oldStateで閉→開・開→閉の遷移だけが分かりましたが、sourceが追加されたことで遷移を引き起こした要素も同じイベントから取れるようになりました。

sourceに何が入るか

sourceにはイベントを引き起こした呼び出し元が入ります。呼び出し元として拾われるのは次の3パターンで、開く方向でも閉じる方向でも同じです。

  • popovertarget属性を持つボタン
  • commandfor + command="show-popover" / "hide-popover" / "toggle-popover"が指定されたボタン
  • スクリプトからshowPopover({ source }) / togglePopover({ source })sourceオプションで渡した要素

sourcenullになるケース

呼び出し元を介さない経路で開閉した場合はnullになります。

  • hidePopover()をスクリプトから呼ぶ(hidePopoversourceオプションを取らない)
  • showPopover() / togglePopover()sourceオプションを取らずに呼ぶ
  • ESCキーや外側クリックによるlight-dismissで閉じる

ユーザー操作起点の発火と区別できるので、リスナー側で分岐させる材料にも使えます。

使い方

メンバー一覧の「詳細」ボタンを押すと、共通のポップオーバーが押されたメンバーの情報に切り替わって表示されます。

event.sourceで発火元のボタンを取得

どのメンバーの「詳細」を押しても同じポップオーバーが開きます。toggleイベントのevent.sourceから発火元のボタンを取って、そのdata-member-idで表示内容を切り替えます。

  • Alice

    Frontend Engineer

  • Bob

    Designer

  • Carol

    Product Manager

最新の toggle イベントから取った情報(open / close 両方)

event.oldState: "—"

event.newState: "—"

event.source.textContent: null

event.source.dataset.memberId: null

ボタンから開いてください

このUIは、ボタン側にpopovertarget属性で共通ポップオーバーを指定し、data-member-idでどのメンバーかを持たせる構成です。開く操作はブラウザに任せられるので、HTMLは次のように書けます。

<button popovertarget="member-card" data-member-id="alice">詳細</button>
<button popovertarget="member-card" data-member-id="bob">詳細</button>
<button popovertarget="member-card" data-member-id="carol">詳細</button>

<div id="member-card" popover></div>

JS側はポップオーバーにtoggleリスナーを1つ張るだけです。event.sourceが押されたボタンを返してくれるので、そのdata-member-idから表示すべき中身を組み立てます。

const popover = document.getElementById('member-card');

popover.addEventListener('toggle', (event) => {
  // 閉じる方向(light-dismissやhidePopover)はsourceがnullのことが多いので、
  // 開く方向だけを処理する
  if (event.newState !== 'open') return;

  const trigger = event.source;
発火元のボタン要素が取れる
  if (!(trigger instanceof HTMLElement)) return;

  popover.replaceChildren(renderMember(trigger.dataset.memberId));
});

ボタンごとに個別のリスナーを張ったり、最後に押されたボタンを別途状態として持ち回したりする必要がなくなり、ポップオーバー側の1か所で発火元を判定できるのがevent.sourceの効きどころです。

おわりに

ToggleEvent.sourceはやっていることは小さいですが、複数トリガーが1つのポップオーバーを共有するUIでは効きの大きい機能です。Baseline 2026で揃ったことで、ボタンごとにリスナーを分けたり、開いたボタンをグローバルに記録したりといった回避策が一段整理できます。Popover APIやcommandforを組み合わせた宣言的なUIと相性がよく、合わせて使うときに覚えておきたい一手です。

0
もくじ