web学习笔记编程技术学习与分享
首页 / 前端开发 / 正文

层叠上下文:z-index 为什么不起作用

2026 年 06 月 05 日约 3 分钟阅读 前端开发

给元素写了 z-index: 9999 却还是被盖住,是 CSS 里最常见的困惑之一。原因几乎总是:它和遮挡它的元素不在同一个层叠上下文里。

层叠上下文是什么

可以把层叠上下文理解成一个独立的三维空间。在这个空间内部,子元素按 z-index 排序;但对外的整体层级,由这个空间的创建者决定。

也就是说,子元素的 z-index 再大,也超不出父级所在的层。

谁会创建层叠上下文

除了根元素,以下情况都会创建新的层叠上下文:

  • position 为 relative/absolute 且 z-index 不为 auto;
  • position: fixed 或 sticky(现代浏览器);
  • opacity 小于 1;
  • transform、filter、perspective 不为 none;
  • will-change 指定了上述属性;
  • isolation: isolate;
  • contain: paint/layout。

最后一条经常被忽略:给卡片加一个 transform: translateY(-2px) 的 hover 效果,它就变成了新的层叠上下文,里面的弹层再也盖不住外面的元素。

一个典型场景

<div class="card" style="transform: translateY(0)">
  <div class="dropdown" style="z-index: 9999"></div>
</div>
<div class="mask" style="z-index: 10"></div>

dropdown 的 9999 只在 card 内部有效。card 本身的层级是 auto(按文档流),所以 mask 会盖住整个 card,包括 dropdown。

解决办法是把弹层挂到 body 下(Portal 模式),或者不要给 card 创建层叠上下文。

调试方法

在 DevTools 里选中元素,看 Computed 面板的 z-index 与 position,再逐级向上检查祖先是否有 transform、opacity、filter。Chrome 的 Layers 面板可以直观看到图层划分。

实践建议

  • 建立一套固定的 z-index 阶梯(如 10 内容、100 悬浮、1000 弹层、2000 提示),不要随手写 9999;
  • 弹层尽量用 Portal 挂到根节点;
  • 慎用 transform 做纯视觉微调,它会带来层叠副作用;
  • 需要隔离时,用 isolation: isolate 显式声明意图,比依赖副作用清晰。
CSS定位层叠