配了三年弹层,每次锚点滚出容器弹层就跟丢了——今天 CSS 自己会追了,anchor-scroll 把这件事彻底原生化了
弹层跟着锚点走,但锚点滚出容器了弹层还在原地——这个坑我踩了三年,今天 CSS 自己会追了,anchor-scroll 把这件事彻底原生化了。
配了三年弹层组件,这个问题始终没找到优雅的解法。以前用 Floating UI 或 Popper,得监听每个祖先容器的滚动事件,然后在每一帧重新计算弹层位置——代码复杂,性能开销也大,页面上几十个弹层同时跑的时候,卡顿非常明显。
Chrome 125 引入的 CSS Anchor Positioning API 把这件事往前推了一大步。
你用 anchor-name 在锚点元素上声明锚点名称:
.trigger {
anchor-name: --my-anchor;
}
然后在弹层上用 position-anchor 引用这个锚点:
.tooltip {
position-anchor: --my-anchor;
position: fixed;
bottom: anchor(top);
left: anchor(center);
translate: -50% 8px;
}
浏览器原生处理定位,不用 JavaScript 算坐标了。但这里有个问题没解决——如果锚点在容器 A 里滚动,弹层在容器 B 里,弹层怎么知道锚点滚去哪了?
anchor-scroll 就是来解决这个问题的。
你把它写在弹层上,告诉它要去监听哪个锚点的滚动:
.boat {
anchor-scroll: --my-anchor;
}
就这么一行,弹层会自动追踪锚点在任意滚动容器里的位置变化。锚点滚到哪,弹层就跟到哪,JavaScript 不用写一行。
这个能力的本质是把滚动同步从 JavaScript 的事件监听里彻底解放出来,让浏览器的布局引擎直接处理。Chrome 官方博客里专门强调过这一点——滚动发生在另一个线程上,必须有办法跨线程追踪,anchor-scroll 就是这个声明式的跨线程方案。
实战场景
想象一个聊天消息列表,每条消息都有个操作菜单按钮,菜单需要跟随消息在列表里滚动,同时不能被列表容器截断。用 position: fixed + anchor-scroll 就能做到:
.message-menu {
position: fixed;
position-anchor: --msg-001;
position-area: top;
anchor-scroll: --msg-001;
z-index: 9999;
}
锚点(消息本身)在列表里正常滚动,菜单用 position: fixed 跳出列表容器,同时靠 anchor-scroll 追踪消息的滚动位置。菜单不会因列表 overflow 被截掉,也不会因为锚点滚出视口而”漂移”。
Chrome 135 的”记住滚动偏移”
ChromeStatus 上有另一个相关进展——CSS Anchor Positioning 的”记住的滚动偏移量”概念。弹层计算尺寸时,需要用”记住的”滚动偏移量,而不是实时滚动的偏移量。否则每次滚动都会触发弹层 resize,性能损伤很大。这个偏移量只在弹层初始化显示或切换 position-try-fallbacks 时才更新,把性能和功能之间的矛盾解开了。
注意:还在试验阶段
Chrome 官方博客里明确说了,anchor-scroll 和 anchor-size() 这些功能还在开发中,可能会随反馈调整。使用时建议用 @supports 做特性检测:
@supports (anchor-scroll: --x) {
.boat {
anchor-scroll: --my-anchor;
}
}
浏览器支持方面,Chrome 125+ 和 Edge 125+ 默认开启,Firefox 和 Safari 还没实现。如果要覆盖更老的浏览器,可以考虑 Oddbird 的 CSS Anchor Positioning polyfill。
说在最后
写弹层跟了三年,踩的坑大概能写一本书。但 CSS Anchor Positioning 这套 API 的出现,让我觉得那些坑可能都是白踩的——布局引擎本来就应该懂「相对定位」这件事,现在它终于懂了。anchor-scroll 把滚动同步这件事也一并收了,声明式地描述锚点关系,浏览器自己处理最复杂的滚动同步,这个方向值得前端工程师提前关注。
评论区
登录后可评论。