给弹窗设置 position: fixed,本该相对视口固定,实际却跟着祖先一起移动。触发条件是祖先上存在 transform、filter、perspective、backdrop-filter、will-change 或 contain 等属性。

包含块被改写
固定定位的默认包含块是视口。一旦祖先的 transform 取值不是 none,该祖先就成为固定定位元素的包含块,偏移基准从视口换成这个盒子。
触发属性一览
| 属性 | 触发条件 |
|---|---|
| transform | 取值不是 none |
| filter | 取值不是 none |
| backdrop-filter | 取值不是 none |
| perspective | 取值不是 none |
| will-change | 声明了上面任一属性 |
| contain | 取 paint 或 layout |
常见场景
页面切换动画给容器加了 transform: translateX(0),看似没有位移,实际已经改写包含块。动画结束后属性没有清除,弹窗定位仍然异常。
处理办法
- 把弹窗节点移到触发元素之外,渲染到 body 或顶层容器。
- 动画结束后移除 transform,不要保留取值为 none 之外的残留写法。
- 改用 popover 顶层渲染,绕开祖先包含块的影响。
排查顺序
- 在计算样式面板逐层向上查看 transform、filter、perspective 的取值。
- 确认 will-change 是否声明了相关属性。
- 确认弹窗节点在 DOM 中的位置,必要时外提。
顺带影响
同一批属性还会创建新的堆叠上下文,z-index 只在上下文内部比较,跨层比较无效。弹窗被压住时同样先查这一层。
