你以为菜单弹层只能用 dialog?今天浏览器用三个属性把这件事彻底原生化了
每次写下拉菜单、工具提示或者浮层卡片,你大概已经习惯了引入 Floating UI 或者写几十行 JS 处理「点击外部关闭」「Escape 键关闭」「焦点管理」这些重复劳动。
2026 年的浏览器已经把这些全部原生化了——而且方案不止一个,用错了你可能会多做三倍的工作。
两个 API,一个选型原则
浏览器给了你两个做浮层的神器:Popover API 和 Dialog API。选对那个,能省掉几百行 JS;选错,你就是在给一个本来不需要 dialog 的场景硬套模态框。
先说怎么判断用哪个。
Dialog API 的 showModal() 造出来的是真正的模态框——背景内容会变「inert」,用户只能和对话框交互,不能点下面的按钮、不能滚动页面、不能做任何其他事情。这适合必须让用户做决策的场景:确认删除、填写表单、输入密码。
除此之外的所有浮层——菜单、工具提示、分享按钮、下拉选择框、通知气泡——都应该用 Popover API。它是轻量的、非模态的,浏览器帮你把一切处理好了,你一行 JS 都不用写。
三行 HTML,浏览器包办所有
这是 Popover API 最让人震惊的地方:一个完整可用的菜单,只需要三行 HTML 属性。
<button popovertarget="menu1">Open menu</button>
<div popover id="menu1">
<p>Menu contents.</p>
<button popovertargetaction="hide">Close</button>
</div>
没有任何 addEventListener,没有任何 getElementById,没有任何 classList.toggle。浏览器自动处理:点击按钮打开浮层、点击外部关闭、按 Escape 关闭、焦点进入浮层、关闭后焦点回到触发按钮。
Popover API 有三个值:
popover="auto"(默认):点击外部自动关闭,Escape 关闭,最常用的模式popover="manual":不自动关闭,必须有显式关闭按钮,适合 toast、通知这类需要用户主动关的场景popover="hint":工具提示专用,同时只显示一个 hint,打开新的旧的就自动消失
它比 dialog 轻在哪
用 Dialog API 做个非模态浮层,你得自己处理一堆事情:ARIA 属性(role="dialog"、aria-modal、aria-expanded)、点击外部关闭(监听 click 事件判断 e.target)、Escape 键关闭(监听 keydown)、焦点 trapping(维护焦点在浮层内)、关闭后焦点返回。
这些 Popover API 全都内置了,浏览器用 accessibility tree 自动关联触发器和浮层的关系,屏幕阅读器直接知道「这个按钮控制这个菜单」,不需要你写任何 ARIA。
和 CSS Anchor Positioning 配合
单独用 Popover,浮层默认在视口居中显示。如果你想让浮层「贴」在触发元素旁边——比如 tooltip 出现在按钮上方——需要搭配 CSS Anchor Positioning。
button {
anchor-name: --menu-trigger;
}
[popover]#menu {
position: absolute;
position-anchor: --menu-trigger;
position-area: top center;
margin-top: 8px;
}
配合 position-try-fallbacks 还能自动处理边界情况——浮层快超出视口时自动翻转到另一侧,这些在 Floating UI 里要写一堆 middleware 的逻辑,用 CSS 三行描述清楚。
什么时候还是用 Dialog
模态框必须用 Dialog API:确认对话框、表单弹窗、需要阻止用户和页面其他内容交互的场景。
Dialog API 的 showModal() 会让背景内容进入 inert 状态,用户无法点击或滚动页面后面的内容。Popover API 不具备这个能力——它是非阻塞的,背景内容仍然可以交互。
如果你在用 Popover 硬套模态行为,你是在给自己挖坑:需要手动处理 inert、需要手动 trap 焦点,很多用 Dialog API 原生自带的东西你都要自己实现一遍。
实际数字
用 Popover API 替代 Floating UI + headlessui 做菜单:42 KB gzipped 的依赖直接归零,不需要任何 JS、没有任何 hydration 抖动。
根据 caniuse 数据:Chrome/Edge 114+、Safari 17+、Firefox 125+,覆盖全球约 93% 的浏览器,2026 年已经可以直接在生产环境用,不需要 polyfill。
下一步
如果你的项目里还在用 Popper.js(3 KB)或 Floating UI(12 KB)做 tooltip 和下拉菜单,现在可以评估迁移了。迁移路径很清晰:
第一步,把不需要模态的浮层从 dialog 改成 popover,看看有多少可以删掉那些 JS 事件监听。第二步,给浮层加上 anchor-name,让它贴着自己的触发元素显示,而不是在视口中间飘着。第三步,给所有用 Popover 的地方加 @supports (popover) 渐进增强,虽然 2026 年覆盖率已经很好了,但多写一步保险。
浏览器已经把浮层这块的需求做完了,你要做的只是决定什么时候用哪个 API。
评论区
登录后可评论。