z-index生效需元素创建层叠上下文,即非static定位且父级未用transform/opacity/filter等截断;fixed元素被遮挡常因body/html的overflow或transform所致;动画需显式初始状态。

z-index 生效的前提是元素自己创建了层叠上下文
很多人给元素加了 z-index: 9999,却发现它还是被盖住——根本原因不是数值不够大,而是这个元素压根没资格参与 z-index 比较。CSS 规定:z-index 只对「已建立层叠上下文」的定位元素起作用。而创建层叠上下文的条件之一,就是 position 值不能是 static。所以光写 z-index 不够,必须同步写 position: relative(或 absolute/fixed/sticky)。
父容器用了 transform/opacity/filter 就会截断 z-index 传递
这是最隐蔽也最常踩的坑。哪怕你的动画元素本身设置了 position: fixed 和 z-index: 9999,只要它的任意一个父级(哪怕隔了好几层)有以下任一 CSS 属性,就会创建新的层叠上下文,把你设的 z-index “锁死”在那个局部范围内:
-
transform值不是none(比如transform: translateZ(0)、scale(1)) -
opacity小于1 -
filter不是none -
will-change声明了transform或opacity
此时,你子元素的 z-index: 9999 只能跟同级兄弟比,无法越过父容器去跟页面其他区域的元素竞争层级。用浏览器开发者工具的「Layers」面板一眼就能看出哪些节点被标为「Stacking Context」。
fixed 元素被遮挡时,优先检查 body/html 的 overflow 和 transform
用 position: fixed 做全屏 loading 或弹窗时,被遮挡往往不是因为 z-index 不够,而是被更底层的渲染限制卡住了:
立即学习“前端免费学习笔记(深入)”;
-
body或html上写了overflow: hidden:移动端 Safari 尤其敏感,会导致 fixed 元素滚动时被裁切 - 某个高阶父容器(如 Vue 根组件、React
App)加了transform: translateZ(0):这会悄悄创建层叠上下文,让所有子级 fixed 元素的 z-index 失效 -
html或body缺少min-height: 100vh:导致 flex 居中失效,进而让margin: auto失效
临时验证方法:在控制台直接删掉疑似有问题的父级元素的 transform 或 overflow,看遮挡是否消失。
动画元素初始状态要显式声明,别依赖默认值
CSS 动画不播放或只闪一下,常是因为元素在动画开始前就不可见,或者浏览器跳过了第一帧。关键点在于初始状态必须可感知、可动画:
- 避免用
display: none控制显示/隐藏——它会中断动画流程;改用opacity: 0; pointer-events: none; - 如果动画含位移(
transform),确保初始transform明确,比如transform: translateX(-20px);,而不是留空 - 伪元素(
::before/::after)做 glitch 效果时,content必须显式设置,否则伪元素根本不渲染 - 用
animation-fill-mode: forwards确保动画结束后保持最终样式,否则可能回退到初始态造成“消失”错觉
真正难调试的,永远不是 z-index 数值本身,而是它在哪一层生效、又被哪一层截断。打开开发者工具,从目标元素一路往上查 Computed 面板里的 transform、opacity、will-change,比盲目调高 z-index 有效得多。


















