🎈 浏览器原生 Popover API 入门
为什么需要 Popover API?
Tooltip 看起来是简单的 UI 问题,但实际上有很多边缘情况:
- 键盘用户 Tab 太快会跳过 tooltip
- 屏幕阅读器可能 announc 两次或根本不 announc
- 鼠标移动太快会闪烁
- 小屏幕上会遮挡内容
- 按 Esc 无法关闭
❌ 旧方式 (库)
- ~60 行 JavaScript
- 5 个事件监听器
- 手动管理 ARIA 状态
- 手动处理焦点
✅ Popover API
- ~10 行声明式 HTML
- 零 JavaScript 事件监听
- 原生 ARIA 状态同步
- 原生焦点管理
快速开始
1. 基本语法
<!-- 触发元素 -->
<button popovertarget="my-tooltip">?</button>
<!-- Popover 元素 -->
<div id="my-tooltip" popover="manual" role="tooltip">
这是 tooltip 内容
</div>
2. popover 属性值
popover="auto": 点击外部自动关闭,焦点移出关闭popover="manual": 只能手动关闭(按 Esc 或调用 hidePopover())
3. 控制行为
<!-- 只打开 -->
<button popovertarget="tip" popovertargetaction="show">显示</button>
<!-- 只关闭 -->
<button popovertarget="tip" popovertargetaction="hide">隐藏</button>
<!-- 切换(默认) -->
<button popovertarget="tip" popovertargetaction="toggle">切换</button>
无障碍支持
🎯 这就是迁移的最大动力!
- 键盘支持: Tab/Shift+Tab 原生工作,Esc 关闭
- 屏幕阅读器: aria-expanded 自动同步
- 焦点管理: 关闭后焦点自动返回触发器
- Lighthouse: ARIA 状态警告消失
仍需 JavaScript 的场景
1. 延迟关闭
// 鼠标移出后延迟 200ms 关闭
let hideTimeout;
const tooltip = document.getElementById('tip');
tooltip.addEventListener('pointerleave', () => {
hideTimeout = setTimeout(() => {
tooltip.hidePopover();
}, 200);
});
tooltip.addEventListener('pointerenter', () => {
clearTimeout(hideTimeout);
});
2. 悬停意图判断
浏览器不知道用户是有意悬停还是路过,这个逻辑仍需 JS。
何时仍用库?
- 大型设计系统: 需要集中管理、一致性
- 复杂定位: 嵌套滚动容器、碰撞检测
- CSS Anchor Positioning: 正在快速获得支持,可替代库
- 团队无障碍经验不足: 库可作为安全网
💡 建议: 先用 Popover API 替换一个 tooltip,感受有多少代码"消失"了。
浏览器支持
- Chrome ✅
- Firefox ✅
- Safari ✅
- 查看详情: Can I Use
相关 API
- CSS Anchor Positioning - 用 CSS 定位 popover
- MDN Popover API
- Open UI 研究