HTML Popover API 实战:用原生属性替代 JS 组件库实现浮层交互
近期在处理一个下拉菜单组件时,我遇到了典型的痛点:不仅要处理展开收起逻辑,还得兼顾焦点管理和无障碍访问(Accessibility)。更别提那无穷无尽的 z-index 层级大战了,移动端上按 ESC 键退出的逻辑经常罢工,至于那个'点击空白处自动关闭'的代码,更是让人头疼。
好在 Popover API 已经在 2025 年 4 月达成了 Baseline Widely Available(基线广泛可用) 状态。这意味着它现在已经在 Chrome、Firefox、Safari 和 Edge 里实现了完美的跨浏览器支持。于是,我决定推翻之前的方案,只用了区区几行纯 HTML 代码就搞定了一切——一行 JS 都没写!
没错,这就是 HTML Popover API。这是一种纯原生、声明式的功能,能让你在完全不碰 JS 的情况下,轻松捏出各种浮层、提示框(Tooltip)、菜单和对话框。
告别手动地狱:纯享版 Popover
在过去,搞个浮层通常需要绝对定位元素,小心翼翼地防着 z-index 冲突,老老实实地写监听器来捕捉外部点击,手撸焦点捕获,加上 ESC 键监听,手动管理 ARIA 属性,最后还要用 JS 把这一大坨逻辑串联起来。
// 传统写法示例
const button = document.querySelector('.trigger');
const popover = document.querySelector('.popover');
let isOpen = false;
button.addEventListener('click', () => {
isOpen = !isOpen;
popover.style.display = isOpen ? 'block' : 'none';
popover.style.zIndex = '9999';
if (isOpen) {
popover.setAttribute('aria-hidden', 'false');
// 还要手写焦点捕获...
// 还要加点击外部关闭监听...
// 还要加 ESC 键监听...
}
});
// 点击外部关闭逻辑
document.addEventListener('click', () => {
(!popover.(e.) && !button.(e.)) {
isOpen = ;
popover.. = ;
}
});

