Tooltip 还在算 getBoundingClientRect?CSS Anchor Positioning 已经能用了,我把 Tooltip、Popover、下拉菜单全用纯 CSS 写了一遍
Tooltip 还在算 getBoundingClientRect?CSS Anchor Positioning 已经能用了,我把 Tooltip、Popover、下拉菜单全用纯 CSS 写了一遍
导语
做 Tooltip、Popover、下拉菜单这些”附着在某个元素旁边”的组件,你是不是也烦透了?每次都拿 getBoundingClientRect() 算坐标,监听 scroll 和 resize 防位置漂移,响应式一改就容易崩。CSS Anchor Positioning API 来了,以后这些组件定位全靠浏览器自己算,JavaScript 只需要管内容。这篇文章我把 3 个最常见的场景用纯 CSS 写了一遍,你可以直接抄。
正文
问题:定位为什么非得 JavaScript 出手
Web 开发里有一个永恒的需求:某个元素(比如 Tooltip、下拉菜单、Popover)要”附着”在另一个元素(按钮、输入框、卡片)的旁边。
传统做法是用 JavaScript:
const rect = button.getBoundingClientRect();
tooltip.style.top = `${rect.bottom + 8}px`;
tooltip.style.left = `${rect.left}px`;
// 还要监听 scroll 和 resize 更新位置
window.addEventListener('scroll', updatePosition);
window.addEventListener('resize', updatePosition);
这套方案的问题很明显:
- 代码量大:至少 30 行 JS + CSS 配合
- 容易忘:scroll 监听加了吗?resize 呢?键盘 focus 呢?
- 性能坑:scroll 事件频繁触发,不做 debounce 就会卡
- 响应式难维护:断点变了位置逻辑要重写
如果浏览器能自动完成”元素 A 相对于元素 B 定位”这件事,那该多好。CSS Anchor Positioning 就是这个”好”。
方案:4 个核心属性 + 2 个函数
CSS Anchor Positioning 的核心思路是:给锚点元素声明一个名字,让浮层元素用这个名字做相对定位。整个过程不需要 JS 参与位置计算。
#### 第一步:给锚点起个名字
.button {
anchor-name: --my-button;
}
anchor-name 接受一个 值,以 -- 开头。这个属性让元素变成一个”锚点”,其他元素可以引用它。
#### 第二步:让浮层绑定锚点
.tooltip {
position-anchor: --my-button;
}
position-anchor 声明这个元素要相对于哪个锚点定位。也可以直接用 HTML 属性:
提示信息
#### 第三步:用 anchor() 函数定位
这是最核心的部分。anchor() 函数让你引用锚点的各个边:
.tooltip {
position: absolute;
/* 浮层的顶部 = 锚点底部 + 8px 间距 */
top: anchor(bottom 8px);
}
anchor() 支持的方向值:
| 值 | 含义 |
|---|---|
top |
锚点的顶部边缘 |
bottom |
锚点的底部边缘 |
left |
锚点的左边缘 |
right |
锚点的右边缘 |
start / end |
逻辑方向(适配 RTL) |
50% |
锚点的中心点 |
#### 第四步:用 anchor-size() 做自适应
有时候你不想写死浮层宽度,想让它的宽度跟着锚点走:
.tooltip {
width: anchor-size(width); /* 和锚点一样宽 */
/* 或者 */
min-width: anchor-size(width, 200px); /* 至少 200px,不超过锚点 */
}
anchor-size() 还支持 height、block、inline 等参数,基本覆盖了尺寸继承的所有场景。
实战 1:纯 CSS Tooltip
.has-tooltip {
anchor-name: --btn;
position: relative;
}
.tooltip {
position-anchor: --btn;
position: absolute;
bottom: anchor(top); /* 浮层底部对齐锚点顶部 */
left: anchor(center); /* 浮层左边缘对齐锚点中心 */
translate: -50% 0; /* 自身水平居中偏移 */
padding: 6px 12px;
background: #1a1a1a;
color: #fff;
border-radius: 6px;
font-size: 14px;
}
对比传统 JS 方案,这里没有一行 JS 代码。浏览器自动处理滚动、resize 时的位置更新。
实战 2:纯 CSS 下拉菜单
.menu-wrapper {
anchor-name: --menu-btn;
position: relative;
display: inline-block;
}
.dropdown {
position-anchor: --menu-btn;
position: absolute;
top: anchor(bottom 8px); /* 在按钮下方 8px */
left: anchor(left);
min-width: anchor-size(width, 180px); /* 至少 180px */
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
list-style: none;
padding: 4px 0;
}
.dropdown li {
padding: 8px 16px;
cursor: pointer;
}
.dropdown li:hover {
background: #f3f4f6;
}
这个下拉菜单的特点是:按钮宽度变了,菜单自动跟着变——因为用了 anchor-size(width, 180px),菜单宽度等于按钮宽度,最小 180px。
实战 3:Popover 自动防溢出
Anchor Positioning 还有个隐藏大招:position-try-fallbacks。当浮层空间不够时,浏览器会自动尝试其他位置。
.tooltip {
position-anchor: --btn;
position: absolute;
position-try-fallbacks: flip-block, flip-inline;
/* 先放下面 → 放不下就翻到上面 → 再不行翻到右边 */
}
/* 定义备选位置 */
@position-try --flip-up {
top: auto;
bottom: anchor(top 8px);
}
@position-try --flip-right {
left: auto;
right: anchor(left 8px);
top: anchor(top);
}
这样你的 Tooltip 在屏幕边缘就不会被截断了——浏览器自动选一个能完整显示的位置。
兼容性:现在能用吗
截至 2026 年 7 月,CSS Anchor Positioning 的浏览器支持情况:
- Chrome 125+:完整支持(2024 年 5 月)
- Edge 125+:跟随 Chromium
- Firefox:尚未支持(flag 后可启用)
- Safari:尚未支持
Chrome 系已经稳定运行超过一年了。如果你的用户以桌面端为主,现在就是上车的时机。Safari 和 Firefox 的兼容方案也很简单——降级用 JS 定位,用 @supports 检测:
/* 降级:JS 控制显隐 */
.tooltip {
display: none;
}
/* 支持 Anchor Positioning 时启用纯 CSS 方案 */
@supports (position-anchor: --x) {
.tooltip {
display: block;
position-anchor: var(--anchor);
}
}
收尾
CSS Anchor Positioning 不是”另一个定位方案”,它是浏览器终于把”元素之间的相对位置”这个逻辑接了过去。以前你写 50 行 JS 才能搞定的事,现在 10 行 CSS 就够了。
如果你手头有 Tooltip、Popover、下拉菜单、悬浮卡片这些组件,建议用这个周末把定位逻辑从 JS 迁到 CSS 上。一个判断标准:如果你的组件定位代码超过 20 行,那现在就是重构它的好时机。
评论区
登录后可评论。













