← Dialog / Popover · 浮层与「关闭」的现代解法 / Popover API:轻量浮层的三种模式 待审核 3 / 9
popover · auto/manual/hint

Popover API:轻量浮层的三种模式

不是所有浮层都需要 <dialog> 那么「重」(模态、锁背景)。菜单、下拉、toast、tooltip 更需要一个轻量、非模态、但同样能进 top layer(无需参与 z-index 竞争)的浮层。这就是 Popover API:给任意元素加一个 popover 属性即可。属性值有三种,行为各不相同。

1 · auto:点外部就关、彼此互斥

两个 popover="auto" 菜单。点按钮打开——注意打开 B 会自动关掉 A(互斥),点浮层外部任意处或按 Esc 也会关(light dismiss)。全程零 JS:按钮用 popovertarget 指向浮层 id 即可。

2 · manual:不会自己关,能叠在一起

popover="manual" 没有 light dismiss,也不互斥——必须显式关闭。这正是 toast 需要的行为:弹出后保持显示,且可以多条同时叠加。下面用 popovertargetaction 指定按钮是 show 还是 hide

3 · hint:浮在 auto 之上、不打断它

popover="hint" 是给 tooltip / 悬停预览准备的第三种值。它和 auto 一样支持 light dismiss(点外部 / Esc 关),但层叠规则被单独拆开,使它能浮在一个已打开的 auto 菜单之上而不把它关掉。Chrome 把 hint 与 auto 的交互简化成一套更可预测的模型,要点有三:

  • 打开 hint 不关掉无关的 auto——auto 菜单开着时照样能弹 hint,菜单不消失(旧模型在某些角落会误关)。
  • hint 只被「新的无关 auto」顶掉——或随它的祖先 auto 一起关闭;此外不受其他 popover 影响。
  • 把 auto 嵌进 hint 不再抛异常——嵌套的 auto 会优雅降级、按 hint 处理。正因如此,可定制的 <select>(其展开列表本身是个 popover)能安全地放进 popover="hint" 里。

4 · toggle 事件:状态变化的钩子

popover 开/关会派发 toggle(以及切换beforetoggle)。事件对象是 ToggleEvent,带 oldState / newState(值为 "open" / "closed")。常用来「打开时才懒加载内容」。

ToggleEvent 还有一个 source 属性,指向触发这次开关的元素——带 popovertarget 的按钮,或带 command / commandfor 的命令按钮。用它能判断「是哪颗按钮打开了我」(例如多个入口共用一个浮层时分流逻辑)。若是 light dismiss(点外部 / Esc)或用 JS showPopover() / togglePopover() 程序化触发,sourcenull

别和 CommandEvent.source 搞混。两者同名 source,但挂在不同事件上:ToggleEvent.source(本页,toggle 事件)给的是触发开关的元素;CommandEvent.source(见 invokers 页,command 事件)给的是发出命令的按钮。ToggleEvent.source 较新——Baseline 2026,晚于 newState;旧浏览器读到 undefined,需做降级。

别在 beforetoggle 里改动 popover 栈。beforetoggle 在开关生效前同步派发,本是用来「定位 / 懒加载」的时机(本页定位菜单正是用它)。若在其中再去 showPopover() / hidePopover() 别的 popover,会对刚要变化的栈造成重入式 mutation。Chrome 收紧了这里的检测,对这类调用更一致地抛 InvalidStateError,以杜绝循环重入。需要「联动开关」就放到 toggle(生效)里做。

popover ≠ dialog。popover 默认非模态:不锁背景、背景仍可交互、不抢焦点。需要「模态、必须先处理完才能碰别的」就用 <dialog>.showModal()。两者可组合:<dialog popover> 同时拿到「可被 popovertarget 声明式触发」和 dialog 的语义。

延伸阅读:The Great CSS Expansion · Popover API · GitButler blog——近年 CSS / HTML 平台能力扩张的全景,其中 Popover API 一节讲清它「为什么能省掉 z-index 与 JS 浮层库」。