很多 CSS 问题会被一句话带偏:“把 z-index 调大一点。”当你把弹窗从 100 改到 9999,仍然被头图、导航、卡片阴影或某个容器挡住时,真正的问题往往不是数值,而是层叠上下文。
这篇文章讲清 z-index 的工作边界,并给一套可复用的排查流程。它适合和 CSS position 定位指南、网页设计布局与视觉层级指南 一起看。
先给结论:z-index 只在同一个层叠上下文里比较
可以把层叠上下文理解成一个局部比较空间。一个元素的 z-index: 9999,如果被关在较低层级的上下文里,可能依然盖不过外面另一个 z-index: 2 的元素。
| 现象 | 常见根因 | 优先排查 |
|---|---|---|
| 弹窗被局部区域挡住 | 父级创建了层叠上下文 | transform、opacity、position |
| sticky 表头盖住下拉菜单 | 两者不在同一层级策略 | 弹层挂载位置 |
| fixed 元素没有全局固定感 | 祖先 transform 影响 | 父级样式 |
| tooltip 被 overflow 裁掉 | 裁剪不是 z-index 问题 | overflow、portal |
不要先加数字,先找比较范围。
一、z-index 生效前提:元素要参与层叠规则
常见情况下,z-index 需要元素具有定位上下文,例如 position 不是默认 static,或者在 flex/grid item 等场景中参与层叠。
.popover {
position: absolute;
z-index: 20;
}
如果你给一个普通静态流元素写 z-index,发现无效并不奇怪。先确认它是不是参与了可比较的层叠规则。
二、哪些属性会创建新的层叠上下文
真实项目里,最容易忽略的是:很多属性会让元素变成自己的“小世界”。
常见触发条件包括:
position配合非 auto 的z-indextransform不为 noneopacity小于 1filter、backdrop-filterisolation: isolatecontain: paintwill-change指向部分绘制属性
很多动画为了开启合成层,会给容器加 transform: translateZ(0)。这能改善某些动画表现,也可能让内部弹层无法盖过外部元素。
三、排查顺序:从遮挡元素和被遮挡元素同时向上找
遇到遮挡问题,不要只看被遮挡的弹层。你要同时检查两条链:
- 被遮挡元素的父级链
- 遮挡元素的父级链
- 两者最近的共同祖先
- 哪个祖先创建了层叠上下文
浏览器开发者工具里可以逐层查看 computed styles。重点搜索:position、z-index、transform、opacity、filter、overflow。
四、弹层组件不要被局部容器困住
下拉菜单、日期选择器、tooltip、modal 这类弹层,经常不应该渲染在触发按钮旁边,而应该挂载到页面根部或统一 overlay 容器。
原因很简单:触发按钮所在的卡片、表格、滚动容器可能有 overflow: hidden 或新的层叠上下文。
更稳定的策略是:
业务组件负责触发 -> 弹层服务负责挂载 -> 全局 overlay 层统一管理 z-index
组件库通常会提供类似 teleport、append-to-body、portal 的能力。使用这些能力不是偷懒,而是让弹层脱离局部布局约束。
五、z-index 要有层级系统,而不是随机数字
一个项目里如果同时出现 10、99、999、9999、2147483647,后期一定难维护。
建议建立层级变量:
:root {
--z-header: 100;
--z-dropdown: 300;
--z-overlay: 600;
--z-modal: 700;
--z-toast: 900;
}
层级系统的价值不是数字本身,而是让团队知道“谁应该盖过谁”。
六、失败案例:弹窗在本地正常,上线后被顶部导航盖住
一个后台页面在本地测试时弹窗正常,上线后发现弹窗半透明遮罩被顶部导航压住。开发者把弹窗 z-index 从 2000 改到 99999,仍然无效。
最后发现:弹窗渲染在页面内容容器内,而内容容器父级因为页面切换动画设置了 transform。导航在另一个更高的层叠上下文里,所以弹窗数字再大也只是在局部范围内比较。
修复方式:
- 把弹窗挂载到应用根节点下的 overlay 容器
- 建立统一 z-index token
- 避免给大范围布局容器添加不必要 transform
- 为弹窗、toast、dropdown 分配明确层级
七、上线前 Checklist
- 项目是否有统一 z-index 层级表
- 弹窗是否挂载到全局 overlay 层
- 祖先元素是否有 transform、opacity、filter
- overflow 裁剪问题是否误判成 z-index 问题
- sticky、fixed、dropdown 是否在移动端测试过
- 是否避免无意义的超大 z-index 数字
结语
z-index 的核心不是“大”,而是“在哪个层叠上下文里比较”。当你先理解层级范围,再设计弹层挂载和层级变量,遮挡问题会从玄学变成可复现、可修复的工程问题。
延伸阅读:


