z-index 是 CSS 里最容易让人误判的属性之一。很多人遇到弹窗被遮挡、下拉菜单显示在内容下面、吸顶导航盖不住图片时,第一反应就是把 z-index 写得更大。但层级问题并不是数字越大就一定越靠上。
真正影响层级的是定位、堆叠上下文、父级关系和绘制顺序。本文从 z-index 的基础规则讲起,再解释堆叠上下文如何产生,以及弹窗遮挡问题该怎么排查。
z-index 的前提
z-index 通常需要元素具备定位或处在特定布局环境中才会生效。常见情况是元素设置了 position,并且值不是 static。
.box {
position: relative;
z-index: 10;
}
如果元素只是普通文档流元素,单独写 z-index 往往不会按预期改变层级。遇到不生效时,先检查定位条件。
数字不是绝对比较
很多人以为 z-index: 9999 一定比 z-index: 10 靠上。实际并不一定。两个元素如果处在不同的堆叠上下文里,比较的是各自父级上下文之间的层级,而不是子元素数字直接全局比较。
这就是为什么有些弹窗写了很大的 z-index,仍然被某个看起来普通的头部或侧边栏挡住。问题通常出在父级堆叠上下文。

堆叠上下文
堆叠上下文可以理解为一个独立的层级世界。里面的子元素先在内部比较层级,然后整个上下文再和外部元素比较。
一旦父元素形成了堆叠上下文,子元素的 z-index 再大,也很难突破父级所在层级。它只能在父级的范围内参与排序。
常见创建条件
很多 CSS 属性都会创建堆叠上下文。常见条件包括:定位元素设置非 auto 的 z-index、position: fixed、position: sticky、opacity 小于 1、transform、filter、will-change、isolation: isolate 等。
.parent {
transform: translateZ(0);
}
上面这个父元素可能会创建新的堆叠上下文。很多动画优化、GPU 加速写法会无意中影响层级,导致弹窗或下拉菜单被限制在某个区域里。
定位上下文
定位上下文和堆叠上下文不是完全一回事,但经常一起出现。绝对定位元素会寻找最近的定位祖先作为定位参考,而层级比较又可能受到堆叠上下文影响。
.parent {
position: relative;
}
.dropdown {
position: absolute;
z-index: 100;
}
如果下拉菜单在父元素内部定位,而父元素又有 overflow: hidden 或较低层级,菜单可能被裁剪或遮挡。这时只改下拉菜单的 z-index 不一定有用。
弹窗遮挡排查
弹窗被遮挡时,第一步不是把 z-index 加到 999999,而是打开浏览器开发者工具,检查弹窗和遮挡元素的父级结构。
重点看:弹窗是否挂在 body 下;父级是否有 transform、opacity、filter、z-index;父级是否有 overflow 裁剪;遮挡元素是否在更高的堆叠上下文里。
挂载位置
很多组件库会把弹窗、浮层、通知、下拉菜单挂载到 body 末尾,这样可以减少被局部父容器影响的概率。如果浮层放在复杂组件内部,就更容易遇到层级和裁剪问题。
推荐思路:弹窗浮层挂载到页面根部
避免:弹窗被放在 overflow hidden 的局部容器里
如果你自己写弹窗组件,也建议让浮层脱离复杂业务容器,统一放到页面根节点附近管理。
overflow 裁剪
有些问题看起来像 z-index,其实是 overflow: hidden 把元素裁掉了。被裁剪的内容不是层级不够,而是根本没有显示出来。
.card {
overflow: hidden;
}
如果下拉菜单在卡片内部,而卡片设置了 overflow hidden,菜单超出卡片部分会被裁掉。此时提高 z-index 无法解决,需要调整结构或挂载位置。
层级规范
大型项目应该建立层级规范,而不是随手写大数字。比如普通内容 1 到 10,吸顶导航 100,浮层 1000,弹窗 2000,消息提示 3000。
:root {
--z-header: 100;
--z-dropdown: 1000;
--z-modal: 2000;
--z-toast: 3000;
}
用变量管理层级,可以避免不同组件互相乱压。后期维护时也更容易判断哪个层级应该高。
负 z-index
z-index 可以是负数,但要谨慎使用。负层级元素可能跑到父级背景下面,甚至不可点击或不可见。
如果只是想做背景装饰,很多时候可以使用伪元素、背景图或普通层级控制,不一定需要负 z-index。负值用多了,排查会非常痛苦。
常见错误
第一种错误是只会加大 z-index,不看父级上下文。第二种错误是父元素无意中设置了 transform,导致子元素层级受限。第三种错误是把浮层放在 overflow hidden 容器里。第四种错误是全项目到处写 9999,没有层级规范。
还有一种错误是把定位问题、裁剪问题和层级问题混在一起。元素位置不对、被裁剪、被遮挡,原因可能完全不同,要分开排查。
实践建议
遇到层级问题时,先确认元素是否满足 z-index 生效条件,再检查是否创建了堆叠上下文。弹窗和全局浮层尽量挂载到 body 下,避免被局部容器限制。下拉菜单如果在卡片内部被裁剪,优先检查 overflow。
不要靠无限加大数字解决问题。建立清晰的层级体系,减少无意创建堆叠上下文的样式,才能让导航、下拉、弹窗、提示等组件长期稳定共存。















暂无评论内容