💬

Popover API — JavaScriptなしでツールチップが作れるようになった理由

popover属性1つで開閉・Escキー・アクセシビリティが自動動作するブラウザネイティブUI

<button popovertarget="tip-1">ホバー</button>
<div id="tip-1" popover="auto" role="tooltip">
  ネイティブツールチップ
</div>

JavaScript 0行。イベントリスナーなし。状態追跡なし。それでもEscで閉じ、キーボードでアクセスでき、スクリーンリーダーが正しく読み上げる。

従来のツールチップが壊れる理由

「ホバーで表示、離れたら非表示」という単純な要件。実際にデプロイすると問題が噴出する。

キーボードユーザーがTabでトリガーに到達してもツールチップが出ない。スクリーンリーダーが2回読むか、全く読まない。マウスを速く動かすとちらつく。小さい画面で重なる。Escで閉じない。フォーカスが失われる。

イベントリスナーが積み重なる。hover/focus別々の処理。外部クリックの特殊ケース。ARIA属性の手動同期。コードが肥大化する。実装の問題ではなく、Webプラットフォームに「この要素はツールチップだ」という概念がなかったからだ。

Popover APIが変えたこと

popover属性を付けるとブラウザがこの要素の役割を理解する。3つが自動で解決。

1. JavaScriptなしで開閉

popovertarget="id"でボタンとpopoverを接続するだけ。クリック、フォーカスでの開閉をブラウザが処理。popovertargetactionshowhidetoggle(デフォルト)を指定可能。

2. Escキー自動処理

グローバルkeydownハンドラ不要。Esc専用のクリーンアップロジック不要。ブラウザがpopoverを解除可能な要素と認識する。

3. ARIA状態の自動同期

aria-expandedがpopoverの開閉に合わせて自動更新。手動管理不要。古い状態が残るリスクがない。

auto vs manual

autoは外部クリック、Esc、別のauto popover表示で自動的に閉じる。ツールチップ向け。

manualは明示的に閉じる必要がある。フォーカス復元も自動ではない。トースト通知向け。

まだJavaScriptが必要な領域

タイミング制御。ネイティブpopoverは即座に開閉する。マウスを速く動かすとちらつく問題は残る。ディレイが必要ならJavaScript。

ホバーインテント。ブラウザはポインターがなぜ要素上にあるか分からない — 意図的か通過中か判断できない。

ただし核心が変わった。JavaScriptはもう相互作用モデル全体を所有しない。Popover APIが基本開閉を処理し、JavaScriptはその上にインテント判別だけを載せる。

ライブラリが依然有効な場合

複雑なポジショニング — ネストしたスクロールコンテナ間の衝突検出、カスタムフリッピングロジックが必要ならFloating UIが依然優位。CSS anchor positioningが登場しつつあるが初期段階。

大規模デザインシステム — チーム横断で一貫した動作とガードレールが必要な場合。

ほとんどの場合 — 単純なツールチップ、ドロップダウンメニュー、トースト通知 — Popover APIだけで十分だ。ライブラリなしで。

キーポイント

1

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

2

aria-expanded自動同期でアクセシビリティバグのカテゴリ自体が消える

3

autoは外部クリック/Escで自動閉鎖、manualは明示的制御 — 用途で選択

4

タイミング制御やホバーインテントは依然JavaScriptが必要だが、核心モデルはブラウザ担当

ユースケース

単純ツールチップ — popover="auto" + role="tooltip"でJS不要 ドロップダウンメニュー — popovertargetactionで開閉制御 トースト通知 — popover="manual"で明示的制御