Z-index

z-index 用来控制元素在 Z 轴上的叠放顺序,也就是多个元素重叠时,谁显示在上面。

可以把页面理解成三维空间:

复制代码
X 轴:左右
Y 轴:上下
Z 轴:前后、遮挡关系

例如:

css 复制代码
.first {
  z-index: 1;
}

.second {
  z-index: 2;
}

当两个元素重叠时,second 通常显示在 first 上面。

基本用法

ini 复制代码
<div class="box first">第一层</div>
<div class="box second">第二层</div>
css 复制代码
.box {
  position: absolute;
  width: 100px;
  height: 100px;
}

.first {
  left: 0;
  top: 0;
  background: red;
  z-index: 1;
}

.second {
  left: 50px;
  top: 50px;
  background: blue;
  z-index: 2;
}

结果:

css 复制代码
蓝色元素的 z-index 更大
所以蓝色元素覆盖红色元素

UnoCSS 写法:

ini 复制代码
<div class="absolute z-10">第一层</div>
<div class="absolute z-20">第二层</div>

任意值:

css 复制代码
<div class="absolute z-[999]">弹窗</div>

z-index 没有单位

正确:

css 复制代码
z-index: 10;

错误:

css 复制代码
z-index: 10px;

z-index 表示层级数字,不是长度,所以不能加 px。

z-index 通常需要配合定位

传统普通元素要让 z-index 生效,通常需要设置:

css 复制代码
position: relative;
position: absolute;
position: fixed;
position: sticky;

例如:

css 复制代码
.box {
  position: relative;
  z-index: 10;
}

UnoCSS:

ini 复制代码
<div class="relative z-10">
  内容
</div>

下面这种写法可能不生效:

css 复制代码
.box {
  position: static;
  z-index: 10;
}

因为 static 是默认定位方式。

不过 Flex 和 Grid 的直接子元素比较特殊,即使没有定位,z-index 也可以生效:

css 复制代码
.parent {
  display: flex;
}

.child {
  z-index: 10;
}

数字越大不一定永远越靠上

很多人会以为:

css 复制代码
z-index: 999999;

一定能覆盖:

css 复制代码
z-index: 10;

实际上不一定,因为 z-index 只能在当前层叠上下文中比较。

什么是层叠上下文

层叠上下文可以理解为一个独立的层级分组。

例如:

css 复制代码
<div class="parent-a">
  <div class="child-a">A 的子元素</div>
</div>

<div class="parent-b">
  B 元素
</div>
css 复制代码
.parent-a {
  position: relative;
  z-index: 1;
}

.child-a {
  position: absolute;
  z-index: 999999;
}

.parent-b {
  position: relative;
  z-index: 2;
}

虽然:

css 复制代码
.child-a {
  z-index: 999999;
}

但它仍然可能被 parent-b 覆盖。

原因是:

css 复制代码
parent-a 整个分组的层级是 1
parent-b 整个分组的层级是 2

浏览器先比较父级分组:

css 复制代码
parent-b 的 2 > parent-a 的 1

因此 parent-b 整体在上面。

child-a 的 999999 只能在 parent-a 内部比较,无法越过父级层叠上下文。

可以理解为:

css 复制代码
页面层叠上下文
├── parent-a:z-index 1
│   └── child-a:z-index 999999
│
└── parent-b:z-index 2

比较顺序是:

css 复制代码
先比较 parent-a 和 parent-b
再比较它们各自内部的子元素

哪些属性会创建层叠上下文

常见情况包括:

css 复制代码
/* 定位元素并且 z-index 不是 auto */
position: relative;
z-index: 1;
css 复制代码
position: absolute;
z-index: 1;

以及:

css 复制代码
position: fixed;
position: sticky;

opacity: 0.9;
transform: translateX(0);
filter: blur(0);
perspective: 1000px;
isolation: isolate;
will-change: transform;

Flex 或 Grid 子元素设置非 auto 的 z-index,也可能创建层叠上下文。

例如:

css 复制代码
.parent {
  transform: translateX(0);
}

即使没有设置 z-index,transform 也可能让它形成新的层叠上下文。

这也是很多"z-index: 9999 为什么不生效"问题的来源。

z-index: auto 和 z-index: 0

默认值是:

css 复制代码
z-index: auto;

auto 表示让浏览器按照默认层叠规则处理,而且通常不会因为这个属性本身创建新的层叠上下文。

css 复制代码
z-index: 0;

表示明确把元素放到当前上下文的第 0 层。对于定位元素、Flex 项目或 Grid 项目,它还可能创建新的层叠上下文。

因此:

css 复制代码
z-index: auto;

和:

css 复制代码
z-index: 0;

并不完全相同。

z-index 相同时怎么办

如果两个元素处于同一个层叠上下文,并且 z-index 相同,通常后面出现的元素显示在上面。

ini 复制代码
<div class="first">第一个</div>
<div class="second">第二个</div>
css 复制代码
.first,
.second {
  position: absolute;
  z-index: 10;
}

因为 second 在 DOM 中更靠后,所以通常会覆盖 first。

更推荐明确设置不同层级:

css 复制代码
.first {
  z-index: 10;
}

.second {
  z-index: 20;
}

负数 z-index

可以设置负数:

css 复制代码
z-index: -1;

表示把元素放到更后面。

但是负数层级容易导致元素跑到父元素背景后面,甚至完全看不见:

css 复制代码
.parent {
  position: relative;
  background: white;
}

.child {
  position: absolute;
  z-index: -1;
}

因此项目中应谨慎使用负数 z-index。

overflow 会影响显示

假设父元素设置:

css 复制代码
.parent {
  overflow: hidden;
}

即使子元素设置:

css 复制代码
.child {
  z-index: 999999;
}

超出父元素范围的部分仍然会被裁剪。

这是因为:

css 复制代码
z-index 控制叠放顺序
overflow 控制裁剪范围

z-index 无法突破父元素的 overflow: hidden。

z-index 不生效时如何排查

1. 检查是否有定位

css 复制代码
position: relative;
z-index: 10;

2. 检查祖先元素

祖先元素是否设置了:

css 复制代码
transform
opacity
filter
z-index
isolation
will-change

它们可能创建新的层叠上下文。

3. 比较父级层叠上下文

不要只比较当前两个元素的 z-index,还要比较它们祖先元素的层级。

4. 检查 overflow

css 复制代码
overflow: hidden;
overflow: clip;

可能把元素裁剪掉。

5. 检查元素是否真正重叠

如果两个元素没有空间重叠,改变 z-index 不会产生明显效果。

推荐的层级管理

不要到处写:

css 复制代码
z-index: 99999;
z-index: 99999999;

最好给项目定义固定层级:

yaml 复制代码
普通内容       0
固定导航       10
下拉菜单       100
遮罩层         900
弹窗           1000
消息提示       1100

例如 UnoCSS:

ini 复制代码
<div className="relative z-0">普通内容</div>
<header className="sticky top-0 z-10">导航栏</header>
<div className="absolute z-100">下拉菜单</div>
<div className="fixed inset-0 z-900">遮罩层</div>
<div className="fixed z-1000">弹窗</div>

最后可以这样记忆:

css 复制代码
z-index 控制的是同一个层叠上下文中的前后顺序

而不是:

复制代码
数字越大,就一定能覆盖页面中的所有元素

另外,层叠上下文和 GPU 合成层不是同一个概念:

复制代码
层叠上下文:决定谁盖住谁
合成层:浏览器的渲染性能优化
相关推荐
CodeMi2 小时前
2026 CSS 接管了这些 JS 的活:6 个 Baseline 特性的落地取舍
javascript·css
gCode Teacher 格码致知7 小时前
前端教学8:提高 CSS 选择器的优先级,有哪些方法?
前端·css·html5
燕落南枝1 天前
CSS 表单选择器与伪元素选择器详解
前端·css
狮子座的男孩2 天前
html+css基础:25、3D变换(3D空间与景深/透视点的位置/位移/旋转/缩放/多重变换/背部可见性)
css·经验分享·html·旋转·透视点·缩放/位移·3d变换/空间与景深
思码梁田2 天前
CSS 浏览器厂商前缀:从「写 5 遍」到现代自动化兼容
前端·css·css3·border-radius·1024程序员节·box-shadow·厂商前缀
feixing_fx3 天前
CSS Grid 入门:二维布局时代的降维打击
前端·css·前端框架·css3
2603_965896623 天前
CSS BEM 命名规范|组件化样式分层权威指南
前端·javascript·css
进击的明明3 天前
前端开发中的 BEM 国际命名规范:让 CSS 不再"打架"
前端·css·html
燕落南枝3 天前
CSS 分组选择器与链接伪类选择器详解
前端·css