css

z-index 为什么失效:理解 Stacking Context

解释 stacking context 的独立分层规则,说明为什么元素写了更大的 z-index 仍然会被遮住,以及 position、opacity、transform、isolation 等属性何时会创建新的层叠上下文。

·更新于 ·阅读约 6 分钟·计算中...
z-index 为什么失效:理解 Stacking Context

z-index: 9999 仍然被盖住,通常不是浏览器坏了,而是你在不同的 stacking context 里比较层级。z-index 不是全页面通用排名,它只在当前层叠上下文内部有意义。

目录

stacking context 是什么

可以把 stacking context 理解成一层独立的分层世界。MDN 对它的描述很明确:每个 stacking context 都是独立处理的,子元素的 z-index 只在自己的父级 context 里有意义,整个 context 再作为一个整体参与上层比较。

所以这句话很关键:

  • 同一个 context 里,z-index 才能直接比较
  • 不同 context 之间,先比较父级那一层,再看子级

什么情况下会创建新的 stacking context

根据 MDN,现在常见的触发条件包括:

  • 根元素 html
  • position: absolute | relativez-index 不是 auto
  • position: fixed | sticky
  • opacity < 1
  • transform
  • filter
  • clip-path
  • perspective
  • isolation: isolate
  • 某些 flex / grid item 配合非 autoz-index

这也是为什么你明明只想“轻微透明一下”或“做个 transform 动画”,结果层级关系突然变了。

为什么 z-index 会“失效”

看一个常见场景:

<div class="card">
  <div class="tooltip">tooltip</div>
</div>

<div class="modal">modal</div>
.card {
  position: relative;
  z-index: 1;
}

.tooltip {
  position: absolute;
  z-index: 9999;
}

.modal {
  position: relative;
  z-index: 2;
}

即便 .tooltip 写成 9999,它也不一定盖得过 .modal,因为 .tooltip 只是 .card 这个 context 的子元素;而 .card 整体只有 z-index: 1,先输给了 .modal2

真正比较关系更像这样:

父级比较:
card (1) < modal (2)

子级比较:
tooltip (9999) 只能在 card 内部比较

transform 和 opacity 为什么经常把层级搞乱

因为它们不只是“视觉效果”,还可能顺手创建新 stacking context。

.panel {
  transform: translateY(8px);
}

或者:

.mask {
  opacity: 0.99;
}

这两种写法都可能让后代元素进入新的层叠边界。所以动画、弹层、拖拽场景里,层级问题经常和 重排与重绘性能 一起出现。

isolation: isolate 什么时候有用

如果你希望一个容器明确成为独立层叠上下文,可以主动写:

.surface {
  isolation: isolate;
}

它适合:

  • 卡片内部有浮层,但不希望和外部混在一起
  • 你要主动切断某些混乱的层级影响

但它不是万能修复键。isolation 只是明确创建新 context,不会自动帮你决定整个页面的层级架构。

排查 z-index 问题的顺序

  1. 先确认元素是否真的有定位或其它创建 context 的条件。
  2. 再看父元素是否已经形成新的 stacking context。
  3. 检查兄弟层之间是谁的父级先赢了。
  4. 最后再决定是调整 DOM 结构、父级层级,还是改成 portal / fixed / top layer 方案。

一个实用判断是:如果你在同一片区域里不断把 z-index20 调到 9999 仍然没用,问题大概率不在“数字不够大”,而在“比较范围错了”。

什么时候不该硬堆 z-index

以下情况通常应该先改结构而不是继续加大数字:

  • 全局 modal 被局部卡片 tooltip 挡住
  • sticky header、drawer、popover 都在各自的局部 context 里竞争
  • 组件库和业务页面各自维护一套随意增长的 z-index 常量

更稳的做法是:

  • 给全局覆盖层留统一层级体系
  • 局部弹层必要时挂到 portal
  • 避免无意义地给父容器加 transform / opacity

React 弹层组件如果已经脱离原有 DOM 层级,下一步还要理解 React Portal 的挂载位置与事件行为。

结论

  • z-index 只在当前 stacking context 内比较。
  • transformopacityposition + z-index 等都可能创建新的 context。
  • 真正“失效”的往往不是 z-index,而是你在错误的层级边界里比较大小。
  • 先查父级 context,再调子元素数字。

参考资料

9 分钟

css中的3d使用:深入理解 CSS Perspective 与 Transform-Style

和是 CSS 3D 变换中不可或缺的两个属性。赋予了场景深度,模拟了观察者的视觉体验;而则决定了子元素如何在 3D 空间中呈现。掌握这两个属性的用法,将为您的网页设计带来更丰富的视觉层次和更具吸引力的交互体验。现在,不妨动手尝试一下,释放您的创意,打造出令人惊叹的 3D 世界吧!

10 分钟

深入理解CSS对齐属性:align-items, align-self, justify-items, justify-self

属性作用对象方向适用布局覆盖关系容器垂直Flex/Grid被align-self覆盖align-self子项垂直Flex/Grid覆盖容器水平Grid被覆盖子项水平Grid覆盖记忆口诀想调垂直用align,想调水平用justify容器设置用-items,单个调整用-selfGri

2 分钟

重排与重绘:CSS 动画为什么会卡

把浏览器渲染中的样式计算、布局、重绘和合成拆开解释,说明哪些 CSS 属性更容易触发布局开销,以及为什么 transform 和 opacity 通常更适合做动画。

订阅 FreeMac

每周精选:免费 Mac 软件评测、可信来源更新、替代方案和少折腾指南。