
z-index: 9999 仍然被盖住,通常不是浏览器坏了,而是你在不同的 stacking context 里比较层级。z-index 不是全页面通用排名,它只在当前层叠上下文内部有意义。
目录
- stacking context 是什么
- 什么情况下会创建新的 stacking context
- 为什么 z-index 会“失效”
- transform 和 opacity 为什么经常把层级搞乱
- isolation: isolate 什么时候有用
- 排查 z-index 问题的顺序
- 什么时候不该硬堆 z-index
- 结论
- 参考资料
stacking context 是什么
可以把 stacking context 理解成一层独立的分层世界。MDN 对它的描述很明确:每个 stacking context 都是独立处理的,子元素的 z-index 只在自己的父级 context 里有意义,整个 context 再作为一个整体参与上层比较。
所以这句话很关键:
- 同一个 context 里,
z-index才能直接比较 - 不同 context 之间,先比较父级那一层,再看子级
什么情况下会创建新的 stacking context
根据 MDN,现在常见的触发条件包括:
- 根元素
html position: absolute | relative且z-index不是autoposition: fixed | stickyopacity < 1transformfilterclip-pathperspectiveisolation: isolate- 某些 flex / grid item 配合非
auto的z-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,先输给了 .modal 的 2。
真正比较关系更像这样:
父级比较:
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 问题的顺序
- 先确认元素是否真的有定位或其它创建 context 的条件。
- 再看父元素是否已经形成新的 stacking context。
- 检查兄弟层之间是谁的父级先赢了。
- 最后再决定是调整 DOM 结构、父级层级,还是改成 portal / fixed / top layer 方案。
一个实用判断是:如果你在同一片区域里不断把 z-index 从 20 调到 9999 仍然没用,问题大概率不在“数字不够大”,而在“比较范围错了”。
什么时候不该硬堆 z-index
以下情况通常应该先改结构而不是继续加大数字:
- 全局 modal 被局部卡片 tooltip 挡住
- sticky header、drawer、popover 都在各自的局部 context 里竞争
- 组件库和业务页面各自维护一套随意增长的 z-index 常量
更稳的做法是:
- 给全局覆盖层留统一层级体系
- 局部弹层必要时挂到 portal
- 避免无意义地给父容器加
transform/opacity
React 弹层组件如果已经脱离原有 DOM 层级,下一步还要理解 React Portal 的挂载位置与事件行为。
结论
z-index只在当前 stacking context 内比较。transform、opacity、position + z-index等都可能创建新的 context。- 真正“失效”的往往不是
z-index,而是你在错误的层级边界里比较大小。 - 先查父级 context,再调子元素数字。
参考资料
继续阅读
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 软件评测、可信来源更新、替代方案和少折腾指南。