层叠上下文是渲染域隔离而非z-index开关;opacity<1、transform非none、filter有值、will-change、isolation:isolate、position:fixed等会隐式创建,导致z-index仅在内部生效且引发性能开销。

层叠上下文不是“让 z-index 生效的开关”,而是「渲染域隔离」——一旦触发,子元素就彻底脱离父级的 z 轴秩序,所有 z-index 比较只在自己小圈子里进行。
哪些 CSS 属性会悄悄创建层叠上下文
你没写 z-index,但元素突然“不听使唤”了,大概率是下面这些属性在背后建了个新“结界”:
-
opacity小于 1(哪怕只是opacity: 0.99) -
transform不是none(包括transform: translateZ(0)、scale(1)这种看似无操作的写法) -
filter有值(哪怕filter: blur(0)或filter: none在某些旧版 Safari 中仍会触发) -
will-change列了任意属性(如will-change: transform),哪怕页面根本没动它 -
isolation: isolate—— 明确声明隔离,但常被忽略 -
position: fixed(Chrome/Edge 下天然触发,无需z-index;Firefox/IE 仍需配合非auto的z-index)
这些不是“可选优化”,而是浏览器强制划分渲染单元的信号。它们不会报错,但会让 z-index 表现失常——比如一个弹窗死活盖不住隔壁卡片,很可能就是卡片父容器写了 opacity: 0.99。
z-index 在层叠上下文里只管“圈内事”
z-index 的数值再大,也跨不出它所属的层叠上下文边界。常见误解是“只要 z 值够高就能压一切”,实际是:
立即学习“前端免费学习笔记(深入)”;
- 父级上下文 A 里有个子元素
z-index: 9999,但它仍被兄弟上下文 B 里的z-index: 1元素盖住——因为 A 和 B 是平级上下文,比较的是 A 和 B 自身在更上一级的顺序,不是它们内部的数字 - 两个同级定位元素,一个加了
transform,一个没加:前者自动升为新上下文,后者留在父级上下文中;此时它们已不在同一“维度”比 z 值 -
z-index: auto的元素永远不会创建新上下文(根元素除外),但一旦父级是上下文,它就被锁死在该上下文的“零层”位置
调试时,用 Chrome DevTools 的 Layers 面板或勾选“Show stacking order”能直观看到上下文嵌套结构,比猜更可靠。
层叠上下文带来的性能隐性成本
每个上下文都可能催生独立合成层(compositing layer),尤其当它包含动画或滚动内容时。问题不在于“多一个上下文”,而在于“多一层嵌套”:
- 连续嵌套的
opacity+transform容易引发“层爆炸”(layer explosion),GPU 内存占用飙升,低端设备掉帧 -
filter和backdrop-filter触发的上下文,会强制其子树离屏渲染(offscreen rendering),开销远高于纯z-index布局 - 用
will-change提前声明,本意是优化,但如果滥用(比如全页面加will-change: transform),反而让浏览器过度分配资源,得不偿失
真正要控制层级时,优先用 position: relative + z-index 组合,而不是靠 opacity: 0.999 或 transform: translateZ(0) “强行提层”——后者是透支渲染管线换来的临时效果。
层叠上下文最危险的地方,是它不报错、不警告、不打断开发流程,却在视觉和性能两个层面同时埋雷。看清哪个元素是“结界发起者”,比调大 z-index 数值重要得多。



















