💬

Popover API — ブラウザネイティブツールチップが変えるもの

JSイベントリスナー、ARIA手動同期、状態管理をHTML属性2つで置き換え

ツールチップを作った経験があれば分かる。見た目はシンプルなのに、実際のユーザーにデプロイすると問題が噴出する。

Tabでトリガーに入っても表示されない。スクリーンリーダーが2回読むか完全に無視する。マウスを速く動かすとチラつく。Escが効かない。フォーカスが消える。

時間が経つと:イベントリスナーが積み重なり、hover/focus別処理、外部クリック特殊ケース、ARIA属性の手動同期...コードが肥大化する。

ライブラリを使った理由

ポジショニングだけで依存性が正当化されるほど複雑だ。スクロールコンテナ、transform、レスポンシブレイアウトでのツールチップ配置は簡単ではない。

だが本当の問題はアクセシビリティだった。マウスユーザーは即時性を、キーボードユーザーは予測可能性を求める。両方を満たそうとすると遅延とエッジケースが生まれる。スクリーンリーダーでの動作が読まれたり、読まれなかったり、2回読まれたり — 非一貫的。

ARIA属性を1つ見落とすとアクセシビリティツリーが混乱する。コードが間違っていたのではなく、プラットフォームに適切なプリミティブがなかった。

Popover API

<button popovertarget="tip-1">ヘルプ</button>
<div id="tip-1" popover="manual" role="tooltip">
  ツールチップの内容
</div>

これだけ。イベントリスナーなし。状態追跡なし。JavaScriptからのARIA更新なし。

フォーカスすると表示、Escを押すと消える。

何が変わるのか

開閉 — ブラウザがHTMLだけで処理する。トリガーとツールチップの関係がマークアップに明示的に存在。

Escキー — キーリスナーを追加していないのに動作する。ブラウザがpopoverの解除可能性を自ら理解。

aria-expanded — popoverの開閉に応じて自動更新。手動管理不要、古い状態が残るリスクもない。

フォーカス復元 — popoverが閉じるとトリガーにフォーカスが自動復元。フォーカス復元コードは追加したのではなく削除した。

コード削減より重要なのは責任の転換だ。以前はJavaScriptが「存在させていた」ツールチップが、今はブラウザがマークアップ上の役割を理解し、フォーカスモデル、アクセシビリティツリー、ネイティブ解除ルールに参加する。

Invoker Commands

popovertarget="id"がボタンをpopoverに接続し、popovertargetactionで動作を指定する。

  • show — 開くだけ

  • hide — 閉じるだけ

  • toggle(デフォルト)— 開いていれば閉じ、閉じていれば開く

1つのツールチップに複数のトリガーを接続できる。

まだ足りない領域

タイミング制御 — ネイティブpopoverは即座に開閉する。マウスの素早い動きやトリガーをかすめると点滅する。hover/focusと表示の間のディレイはまだJavaScriptが必要。

ホバーインテント — ブラウザはホバーが意図的か、ポインターが通過中かを判断できない。非表示前の短いディレイ、ポインターがツールチップに移動時の解除キャンセルはJavaScriptの領域。

popover="manual"のフォーカス — autoと違い、manualではフォーカスの自動復元がない。blurで閉じる時はトリガーへ明示的にフォーカスを返す必要がある。

ライブラリが有効な場合

大規模デザインシステムでは集中化された動作、一貫したデフォルトのためにライブラリが合理的。ネストされたスクロールコンテナやカスタムフリッピングロジックのような複雑なポジショニングにはFloating UIのようなツールが依然有利。

CSS anchor positioningがこの問題のかなりの部分をカバーし始めている。純粋CSSでトリガー基準の相対配置、ビューポート認識配置、エッジフリッピングが可能になりつつある。Interopに含まれ完全なクロスブラウザサポートの見通しがある。

デフォルトがネイティブAPIに移行中だ。最もシンプルなツールチップが同時に最も正確なツールチップになり得る初めての時点。

従来方式 vs Popover API

項目 従来(JavaScript) Popover API
開閉 イベントリスナー+状態変数 HTML属性のみ
Escキー処理 手動keydownリスナー ブラウザ自動
aria-expanded JSで手動同期 自動同期
フォーカス復元 手動復元コード 自動復元(auto)
タイミング/ディレイ JSで制御 まだJS必要
ポジショニング Floating UI等 CSS anchor positioning(進行中)

最小実装コード

<button popovertarget="tip-1">ヘルプ</button> <div id="tip-1" popover="manual" role="tooltip"> ツールチップの内容 </div>

JavaScript 0行。イベントリスナー0個。ARIA手動更新0件。

popovertargetactionオプション

toggle デフォルト。開いていれば閉じ、閉じていれば開く。
show 開くだけ。すでに開いていれば何もしない。
hide 閉じるだけ。すでに閉じていれば何もしない。

popover="auto" vs popover="manual"

動作 auto manual
外部クリックで閉じる 自動 不可
Escで閉じる 自動 自動
他のpopoverで自動閉じ 自動 不可
フォーカス自動復元 自動 手動必要

CSS Anchor Positioning — 次のステップ

ポジショニングライブラリが解決していた問題をCSSがカバーし始めている。トリガー基準の相対配置、ビューポート認識配置、エッジフリッピングが純粋CSSで可能になりつつある。Interopに含まれクロスブラウザサポートの見通しがあるが、まだ新しい機能のためプロダクション使用はケースバイケースで判断が必要。

キーポイント

1

popover属性+popovertarget属性だけで開閉、Escキー、キーボードナビゲーションが自動動作

2

aria-expandedがpopover開閉に応じて自動同期 — ARIAバグのカテゴリ自体が除去

3

タイミング制御、ホバーインテント判別はまだJavaScript必要 — コアインタラクションのみブラウザ担当

4

CSS anchor positioningがポジショニング問題もカバー開始 — Interop含有でクロスブラウザサポート見通し

ユースケース

シンプルツールチップ — JavaScript0行でアクセシビリティ完璧なネイティブツールチップ ドロップダウンメニュー — popovertargetaction="toggle"で開閉+Esc自動解除 デザインシステムマイグレーション — 既存カスタムツールチップからネイティブAPIへ段階的移行