你以为菜单弹层只能用 dialog?今天浏览器用三个属性把这件事彻底原生化了

每次写下拉菜单、工具提示或者浮层卡片,你大概已经习惯了引入 Floating UI 或者写几十行 JS 处理「点击外部关闭」「Escape 键关闭」「焦点管理」这些重复劳动。

2026 年的浏览器已经把这些全部原生化了——而且方案不止一个,用错了你可能会多做三倍的工作。

两个 API,一个选型原则

浏览器给了你两个做浮层的神器:Popover APIDialog 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-modalaria-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。

评论区

0 条评论

登录后可评论。