z-index 失效是因为父容器的 transform(非 none)触发了新层叠上下文,子元素 z-index 仅在该上下文内生效,无法与外部元素跨上下文比较层级。

z-index 为什么在父容器加了 transform 后就失效了
因为 transform(非 none 值)会触发新层叠上下文,子元素的 z-index 只能在该上下文内部生效,无法和外部同级元素比高低。
常见现象:一个弹窗组件明明设置了 z-index: 9999,却被旁边一个带 transform: translateY(2px) 的卡片遮住——不是弹窗层级不够,是它被“关进”了卡片创建的层叠牢笼里。
- 触发层叠上下文的属性不止
transform,还包括opacity: 0.99、filter: blur(1px)、will-change: transform、backdrop-filter等 - 只要父级一触发,子元素再高的
z-index也出不去;哪怕父级z-index是auto或没设position,照样成立 - 调试时打开 Chrome DevTools → 「Layers」面板,或勾选「Render layer borders」,能直观看到哪些元素成了独立层叠上下文
如何判断某个元素是否创建了层叠上下文
不需要背全部规则,只盯住几个高频触发点就行:看它有没有设置以下任一属性(且值不为默认/初始值):
-
position为relative/absolute/fixed/sticky,且z-index是具体数字(非auto) -
opacity小于1 -
transform不为none -
filter不为none -
will-change设了可触发的属性(如transform、opacity) isolation: isolate
注意:position: relative 单独存在不会创建层叠上下文,必须搭配非 auto 的 z-index 才会;而 opacity: 0.99 单独写就足够触发。
立即学习“前端免费学习笔记(深入)”;
定位元素层级被“卡住”时该怎么调
不是盲目堆高 z-index,而是先理清层级归属关系。两个关键动作:
- 把需要“浮出来”的元素(比如模态框)提升到更高一级的层叠上下文中——通常做法是让它脱离问题父容器,挂到
body下,或确保其最近的已定位祖先没有意外触发层叠上下文 - 如果必须保留在当前结构中,就把干扰性的触发属性(如
filter、opacity)下移到子元素,而不是套在父容器上 - 检查父容器是否用了
flex或grid,且子元素设置了z-index:这种组合也会隐式创建层叠上下文,尤其在旧版 Safari 中更敏感
为什么 z-index: 1 有时比 z-index: 999 还靠前
因为它们根本不在同一个层叠上下文里。前者可能属于根层叠上下文(html),后者却困在某个 opacity: 0.5 的卡片内部——就像县级干部再努力也升不到省级编制里。
真正决定谁在上面的,是「父级的层叠等级」,不是子元素自己的 z-index 数值。所以排查时第一反应不该是“改大点”,而是“它爹是谁?它爹有没有权限管这事?”
最易被忽略的一点:层叠上下文一旦创建,就不可撤销。你没法用 z-index: auto 或删掉 transform 来“关闭”它——只能从结构或样式上绕开,或者重置触发条件(比如把 opacity: 0.99 改成 1)。


















