弹层坐标我算了五年,现在浏览器自己会找——Popover API + CSS Anchor Positioning 把这件事彻底原生化了
做弹层、下拉菜单、tooltip,我前前后后折腾了五年。每次都要写一堆 getBoundingClientRect()、算 offsetTop/offsetLeft、还要处理屏幕边缘被截的问题。Popper.js 用了三年,越用越觉得这个依赖不该存在。
2026 年,浏览器终于给了两个原生答案,而且它们可以配合使用:Popover API 负责弹层的显示/隐藏机制,CSS Anchor Positioning 负责弹层的位置计算。两者加在一起,以前那堆 JS 全可以删掉。
先说 Popover API——这才是弹层的正确打开方式
以前写一个 tooltip,要先写 HTML,再写 CSS 隐藏,然后用 JS 监听 hover/focus,计算位置,最后手动处理点击外部关闭。代码写出来二十多行,逻辑还一堆 bug。
用 Popover API,三行 HTML 就够了:
<button popovertarget="mytooltip" popovertargetaction="show">hover me</button>
<div id="mytooltip" popover>Tooltip content</div>
加个 popover 属性,浏览器自动帮你做三件事:
- 页面加载时隐藏(相当于
display: none) - 点击按钮显示,把元素提升到 Top Layer(永远在最上层,不会被别的内容截掉)
- 点击弹层外部自动关闭(light dismiss)
不需要写一行 JS。你甚至可以用 popovertargetaction 属性指定按钮是显示还是隐藏:
<button popovertarget="mymenu" popovertargetaction="show">打开菜单</button>
<button popovertarget="mymenu" popovertargetaction="hide">关闭菜单</button>
<div id="mymenu" popover>菜单内容</div>
popover 属性有三个值:auto(默认,支持轻触关闭)、hint(不响应点击外部关闭)和 manual(完全不自动关闭)。大多数场景用 auto 就够了。
再说 CSS Anchor Positioning——定位终于不用 JS 算
Popover API 解决了显示/隐藏的问题,但弹层出现在哪里、弹层紧贴着哪个按钮——这部分它不管。这个任务交给 CSS Anchor Positioning。
用法很简单。首先给触发元素和弹层元素建立关联:
.trigger-btn {
anchor-name: --my-anchor;
}
.tooltip {
position-anchor: --my-anchor;
position: absolute;
}
然后用 position-area 指定弹层相对于锚点出现的位置。position-area 支持一套类似九宫格的值:
.tooltip {
position-anchor: --my-anchor;
position: absolute;
position-area: top center;
}
这行 CSS 意思是:把弹层放在锚点元素的上方,水平居中对齐。如果上方空间不够被屏幕边缘截掉,浏览器会自动尝试备选位置——比如改到下方。
备选位置用 @position-try 声明:
@position-try --flip-above-below {
position-area: bottom center;
}
.tooltip {
position-anchor: --my-anchor;
position: absolute;
position-area: top center;
position-try-fallbacks: --flip-above-below;
}
这段的意思是:优先显示在上方,如果上方空间不够(溢出视口),自动切换到下方显示。全程不需要 JS 计算。
两个 API 合在一起:零 JS 的完整弹层方案
单独用任何一个都觉得缺了点什么,但合在一起,就是一套完整的弹层方案:
<style>
.trigger {
anchor-name: --menu-btn;
}
.menu {
position-anchor: --menu-btn;
position: absolute;
position-area: bottom left;
@position-try --flip-right {
position-area: bottom right;
}
position-try-fallbacks: --flip-right;
}
</style>
<button class="trigger" popovertarget="mymenu">打开菜单</button>
<div id="mymenu" class="menu" popover>
<ul>
<li>选项一</li>
<li>选项二</li>
<li>选项三</li>
</ul>
</div>
这就是全部代码。没有 JS、没有第三方库、没有 getBoundingClientRect。点击按钮显示菜单,菜单自动出现在按钮下方,如果右边空间不够自动切到左边——浏览器自己算。
interest invokers:hover/focus 也能不用 JS
Popover API 还支持一个有意思的功能:interest invokers。用它可以做到 hover/focus 触达弹层显示,连 click 都不需要:
<button interestfor="tip1">hover 显示 tooltip</button>
<div id="tip1" popover>hover 时显示的内容</div>
interestfor 属性让元素在 hover/focus 时自动触发目标 popover,移开时自动关闭。tooltip 场景用这个最合适。
为什么说 popper.js 可以删了
不是说 popper.js 不好——它解决了很重要的问题。只是这个问题现在浏览器原生解决了。
以前 popper.js 要解决三个问题:定位计算、边缘检测、层级管理。定位计算现在 CSS Anchor Positioning 做了;边缘检测 @position-try 做了;Top Layer 让弹层永远在最上层,不需要自己调 z-index。这三个加在一起,popper.js 提供的核心价值已经被浏览器覆盖了。
实际项目里,把 popper.js 删掉之后,bundle 体积少了 14KB(gzip),弹层逻辑的代码行数从 60 行降到 15 行,而且没有状态管理的负担。
浏览器支持情况
截至 2026 年,Popover API 和 CSS Anchor Positioning 都已进入 Baseline,覆盖率:Chrome 117+、Firefox 125+、Safari 17.4+。主流项目可以直接用,不需要 polyfill。
下一步你可以做什么
- 打开任意一个用了 popper.js 或 floating-ui 的项目
- 把弹层元素的 HTML 加上
popover属性 - 把 JS 里的定位逻辑换成 CSS
position-anchor+position-area - 把
@position-try声明加进去,处理屏幕边缘的情况 - 删掉 popper.js 依赖
这四步做完,你的弹层就彻底脱离 JS 了。
评论区
登录后可评论。