配了八年浮动层,每次定位都要靠 JS 算坐标——今天 CSS anchor() 把这件事彻底原生化了
每次给浮层(tooltip、下拉菜单、弹出卡片)定位,都是同一套流程:先调 getBoundingClientRect() 拿到触发器和浮层各自的尺寸,再算相对位置,然后挂一个 ResizeObserver 监听变化,滚动的时候还要再算一遍。二十年了,这个流程几乎没变过。
CSS 锚点定位(CSS Anchor Positioning)把这个流程彻底原生化了。一个元素声明为「锚点」,另一个元素声明「跟着这个锚点走」,浏览器自己算坐标、自己在滚动和 resize 的时候更新。
anchor() 核心语法
触发器用 anchor-name 声明自己是锚点,浮层用 position-anchor 声明跟着谁走:
/* 触发器:声明自己是一个锚点 */
.trigger {
anchor-name: --my-tooltip;
}
/* 浮层:声明跟着哪个锚点走,并描述相对位置 */
.tooltip {
position-anchor: --my-tooltip;
/* 浮层底边对齐触发器顶边,水平居中 */
bottom: calc(anchor(top) + 8px);
left: anchor(center);
translate: -50% 0;
}
anchor() 函数接受两个参数:锚点名和边(top/bottom/left/right/center/start/end),返回锚点对应边的坐标。浮层定位全程浏览器自动处理,零 JS。
溢出交给 position-try-fallbacks
光定位还不够,浮层贴着视口边缘的时候浏览器怎么处理?
position-try-fallbacks 定义一组备选位置,浏览器自己判断当前是否会溢出、应该用哪个:
.tooltip {
position-anchor: --my-tooltip;
bottom: calc(anchor(top) + 8px);
left: anchor(center);
translate: -50% 0;
/* 浏览器自动判断:如果上方溢出,就翻转到底部 */
position-try-fallbacks: flip-block;
}
flip-block 翻转块轴方向(上变下),flip-inline 翻转内联轴方向(左变右),两者连用对角线翻转。如果预置关键字不够用,可以用 @position-try 自定义完整回退规则:
@position-try --below {
top: calc(anchor(bottom) + 8px);
bottom: unset;
}
@position-try --right {
left: calc(anchor(right) + 8px);
bottom: unset;
top: anchor(center);
translate: 0 -50%;
}
.tooltip {
position-try-fallbacks: --below, --right;
}
浏览器按顺序尝试,第一个完全放进视口的就是最终位置,一个都不合适就保留原始位置。
配合 Popover 如虎添翼
CSS 锚点定位和 Popover API 是天然搭档:
.trigger {
anchor-name: --menu-trigger;
}
#menu {
position: absolute;
position-anchor: --menu-trigger;
top: calc(anchor(bottom) + 4px);
left: anchor(left);
position-try-fallbacks: flip-block;
}
不需要任何 JS 就能做到:点击按钮打开菜单、浮层跟着按钮走、贴近视口边缘自动翻转。Popover 负责开/关状态、焦点陷阱和轻关闭行为,anchor 定位负责找准自己的位置。
浏览器支持情况
Baseline 2026(2026 年 1 月起),Chrome 125+、Edge 125+、Safari 26+、Firefox 147+ 支持,约 83% 全球覆盖率。Safari 26 之前需要 @supports 降级,Firefox 暂不支持 named @position-try 规则。
/* 降级方案 */
.tooltip {
position: absolute;
top: 100%;
left: 50%;
translate: -50% 0;
}
@supports (anchor-name: --x) {
.tooltip {
position-anchor: --my-tooltip;
position-area: bottom;
position-try-fallbacks: flip-block;
}
}
三个坑
命名必须以 — 开头。 anchor-name 的值是 CSS 自定义属性格式,必须是 –xxx,不是 –xxx 浏览器会静默当这个锚点不存在。
flip-block 不够用就用组合。 浮层贴在角落的时候,单轴翻转可能还是溢出一个方向,组合 flip-block flip-inline 才是全解。
Firefox 暂不支持 named @position-try。 如果你的回退逻辑很复杂,用的是自定义 @position-try 规则而不是 flip 关键字,Firefox 用户会看到原始位置,可能溢出。
下一步
三步落地:
- 打开 DevTools,在任意一个 tooltip 上试一行
anchor-name: --test,确认浏览器解析正常 - 选一个不复杂的浮层(如下拉菜单),用锚点定位替代现有 JS 逻辑,不要贪多,先跑通一个
- 如果你在用 Popper.js,可以开始计划迁移路径了,锚点定位能覆盖它 80% 的核心功能
CSS 把 JS 干了这件事,这几年一直在发生。这次是二十年最难解决的问题之一。
评论区
登录后可评论。