你以为 tooltip 只能靠 JS 定位?今天 CSS Anchor Positioning 把这件事彻底原生化了
配了五年浮层防溢出,每次新写一个 tooltip 都要装 Floating UI(或者老的 Popper.js),然后配 autoUpdate、flip、shift、arrow 一整套 middleware,监听 scroll/resize/window-resize,最后在 React 组件里 useFloating() 返回的 floatingStyles 再写到 style 里。代码写了 20 行,运行起来每帧还在主线程跑 getBoundingClientRect。今天 CSS Anchor Positioning 把这件事彻底原生化了。
核心就一件事:给触发元素起个名字(anchor-name),浮层元素声明”我要绑到哪个名字”(position-anchor),浏览器在渲染引擎里算位置,完全不占 JS 主线程。
以前用 Floating UI 写的 tooltip:
import { useFloating, autoUpdate, offset, flip, shift, arrow } from '@floating-ui/react';
function Tooltip({ children, content }) {
const { refs, floatingStyles } = useFloating({
placement: 'top',
middleware: [offset(8), flip(), shift(), arrow({ element: arrowRef })],
whileElementsMounted: autoUpdate,
});
return (
<>
<button ref={refs.setReference}>{children}</button>
<div ref={refs.setFloating} style={floatingStyles}>{content}</div>
</>
);
}
现在用 CSS Anchor Positioning:
function Tooltip({ children, content }) {
return (
<>
<button>{children}</button>
<div className="tooltip">{content}</div>
</>
);
}
button {
anchor-name: --my-tooltip;
}
.tooltip {
position: absolute;
position-anchor: --my-tooltip;
inset-area: top center;
margin-bottom: 8px;
}
三行 CSS,删掉一个 npm 依赖,useFloating 不需要了,floatingStyles 不需要了,whileElementsMounted 不需要了。
position-area:网格速写,比 top/left/right/bottom 更直观
inset-area: top center 意思是”浮层放在锚点上方、水平居中”。完整的网格:
top left | top | top right
———+————-+———–
left | center/span | right
———+————-+———–
bottom lt| bottom | bottom right
比 bottom: calc(anchor(top) + 8px); left: anchor(center); translate: -50% 0 短多了。
溢出自动处理:position-try-fallbacks
浮层在视口边缘会溢出,flip middleware 的能力现在用 CSS 声明:
.tooltip {
position-anchor: --my-tooltip;
inset-area: top center;
position-try-fallbacks: flip-block, --flip-below;
}
@position-try --flip-below {
inset-area: bottom center;
}
flip-block 是内置关键字(不用写 @position-try),浏览器自动尝试”上方→下方”的 fallback。–flip-below 是自定义命名 fallback,可以定义任意位置策略。
配合 Popover API 零 JS 完整方案
浮层还需要点击显示/隐藏、手动关闭、背景遮罩?配合 HTML popover 属性:
<button popovertarget="menu">Open</button>
<div id="menu" popover>
<ul>
<li><a href="#">Option 1</a></li>
<li><a href="#">Option 2</a></li>
</ul>
</div>
popover 给浮层加了 show/hide 切换、ARIA 推断、点击外部关闭。CSS Anchor 给它加上”贴在某个元素旁边”的能力。两件事分开处理,各司其职,比以前全塞在 JS 里清晰得多。
性能可测量:删包大小、减主线程占用
- Popper.js + 依赖:~15-20KB gzipped
- Floating UI 核心:~8-12KB gzipped
- CSS Anchor Positioning:0KB
主线程收益:Floating UI 在每次 scroll/resize 时 JS 重新算坐标,滚动帧里大概 2-4ms。CSS Anchor 全部在浏览器渲染引擎里走,不占 JS 主线程。Chrome DevTools Performance 面板里能看到 scroll 时 floating-ui 相关脚本消失。
浏览器支持(2026 年):
- Chrome 125+ / Edge 125+:已稳定两年
- Firefox 131+:已稳定
- Safari 18.2+:已稳定
- 全球覆盖:约 94%,已是 Baseline 2026
Safari 18.2 之前(iOS 18.1 及更早)需要 @supports (position-anchor: –x) 渐进增强,或者用 tiny polyfill。
迁移步骤:
- 搜索项目里 useFloating / @floating-ui/react / popper.js 的使用量
- 挑一个最简单的 tooltip 练手:先加 anchor-name,再改 CSS
- whileElementsMounted: autoUpdate 可以直接删——锚点位置随滚动自动更新,不需要监听
- flip/shift middleware → position-try-fallbacks: flip-block, flip-inline
- 复杂自定义定位 → 写 @position-try –custom-name { inset-area: … }
- popover 属性加在根 div 上,show/hide 逻辑完全不用 JS
CSS Anchor Positioning 不会替代所有 JS 浮层场景——需要跟随鼠标、任意坐标定位、精确尺寸测量的,还是需要 JS。但 90% 的 tooltip/popover/dropdown 场景,这个 API 已经够用了,而且够得刚刚好。
评论区
登录后可评论。