opacity小于1、transform不为none、filter不为none、will-change指定相关属性、isolation: isolate会隐式创建堆叠上下文;position需配合非auto的z-index(如z-index: 0)才显式触发。
不是只有 z-index 才能触发堆叠上下文,很多看似“无关”的属性会悄悄创建新上下文,导致子元素的 z-index 失效。最常踩坑的是:
opacity 小于 1(哪怕只是 opacity: 0.99)transform 不为 none(例如 transform: translateZ(0)、translateX(0)、scale(1))filter 不为 none(哪怕 filter: blur(0))will-change 指定了 transform、opacity、top 等属性isolation: isolate这些属性一加,父容器立刻变成独立“层叠世界”,里面再大的 z-index 也出不去。
position: relative/absolute/fixed/sticky 本身不创建上下文;只有加上非 auto 的 z-index(比如 z-index: 0、z-index: 1)才会触发。注意:z-index: auto 是默认值,它不创建上下文,也不参与显式排序。
常见误解:
立即学习“前端免费学习笔记(深入)”;
position: relative; z-index: auto → ❌ 不创建上下文position: relative; z-index: 0 → ✅ 创建上下文(别小看这个 0)position: absolute; z-index: 1 → ✅ 创建上下文(且自身在该上下文中排第 1)也就是说,z-index: 0 和没写 z-index 完全是两回事——前者锁死后代层级范围,后者让元素按 DOM 顺序自然堆叠。
靠猜不如看实锤。Chrome DevTools 最可靠的方式是:
stacking context 是否标记为 true
Paint flashing 或 Layer borders,观察渲染分层是否异常分裂如果发现一个轮播容器明明没设 z-index,但内部按钮就是盖不到导航栏上,十有八九是它用了 transform: translateZ(0) —— 这种“性能优化”反而成了层叠陷阱。
再大的 z-index(比如 999999)也救不了嵌套太深的浮层。根本解法是:把模态框、Tooltip、Toast 这类全局浮层直接挂到 <body> 下,脱离任何可能创建上下文的父容器。
原因很直接:
<html>)层级最高,没有父级能压它opacity: 0.99 或 transform,整块内容被“关进”低层级上下文createPortal,Vue 中可用 Teleport 实现 DOM 提升数值膨胀是表象,结构失控才是根源。一个 z-index: 2147483647 的按钮,如果卡在三层 transform 套娃里,照样会被 z-index: 10 的 <header> 盖住——因为它的整个“世界”都矮了一截。