Menu

CSS z-index:层叠顺序与层叠上下文

z-index 决定重叠的元素中哪个绘制在最上面。本文讲解它对哪些元素有效、层叠上下文如何把元素分组,以至于 z-index 9999 也可能输掉、哪些属性会创建层叠上下文,以及如何修复不起作用的 z-index。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

z-index 的作用

当元素重叠时,z-index 决定哪个绘制在前面。数字大的胜出。它对定位元素(position 不是 static 的元素)以及 flex 和 grid 项目有效:

没有 z-index 时,HTML 中靠后的元素会绘制在靠前的元素上面,所以 C 会盖住 B,B 会盖住 A。这里的 z-index 值把顺序反了过来:A 在最前面,然后是 C,再然后是 B。

取值

值含义
auto(默认)和兄弟元素一起位于第 0 层,不创建层叠上下文
0第 0 层,但会创建层叠上下文
正整数位于较小值的前面
负整数位于第 0 层后面;如果父元素形成了层叠上下文,还会位于父元素内容的后面

只有整数有效:z-index: 1.5 会被忽略。

为什么 z-index 9999 也会输:层叠上下文

z-index 的值并不是在整个页面范围内比较的。元素被分组到层叠上下文中,z-index 只给同一个上下文中的元素排序。然后这个上下文本身作为一个整体层,按创建它的元素的 z-index 放置。

下面,蓝色下拉菜单的 z-index: 9999,但它位于一个 z-index: 1 的头部里。绿色横幅是这个头部的兄弟元素,z-index: 2。整个头部层,包括下拉菜单在内,都在横幅下面:

点击按钮。设为 z-index: auto 后,头部不再形成层叠上下文,所以下拉菜单的 9999 直接和横幅的 2 比较,下拉菜单就到了最前面。把下拉菜单提高到 99999 是永远不会有用的。

什么会创建层叠上下文

满足下面任何一条,元素就会成为自己的层叠上下文,把它内部所有元素的 z-index 都困在里面:

属性条件
position: relative 或 absolutez-index 不为 auto
position: fixed 或 sticky始终
flex 或 grid 项目z-index 不为 auto
opacity小于 1
transform、scale、rotate、translate任何不为 none 的值
filter、backdrop-filter任何不为 none 的值
isolationisolate
mix-blend-mode任何不为 normal 的值
will-change指定了上面任一属性
containlayout 或 paint(以及 strict、content)

出人意料的是 opacity 和 transform:给父元素加一个淡入效果或悬停动画,可能会让它的下拉菜单突然跑到下一个区块的后面。

提示框的 z-index: 10 本应让它位于下一列之上。但 opacity: 0.99 让第一列成为 z-index 为 auto(第 0 层)的层叠上下文,而第二列在 HTML 中位于同一层的后面,所以它会画在整个第一列之上,提示框也包括在内。删掉 opacity 那一行,提示框就又回到前面了。

负 z-index

负 z-index 会把元素放到它的兄弟元素后面。如果父元素没有形成层叠上下文,它甚至会跑到父元素背景的后面,这常常让人意外。在父元素上设置 isolation: isolate 会创建一个上下文,而不带来任何其他视觉效果,从而把负值的子元素留在里面:

没有 isolation 时,琥珀色边框掉到了按钮自身的灰色背景后面,只露出边缘。设置 isolation: isolate 后,边框留在按钮的层叠上下文里:在背景之上,在文字之下。

z-index 层级规范

为整个网站挑选几个命名的层级,而不是不断加大数字:

:root {
  --z-dropdown: 10;
  --z-sticky: 20;
  --z-overlay: 30;
  --z-modal: 40;
  --z-toast: 50;
}
.modal { position: fixed; z-index: var(--z-modal); }

大多数层级问题都是层叠上下文的问题,而不是数字的问题,所以一套小的层级规范,再加上在内部使用 z-index 的组件上设置 isolation: isolate,就能解决大部分问题。z-index 需要定位才能生效;position 页面讲解了定位。

常见错误

  • 给静态元素设置 z-index。加上 position: relative(或让它成为 flex 或 grid 项目)。
  • 一路加到 9999。如果父元素形成的层叠上下文位于竞争对手下方,子元素的值再大也赢不了。应该去找出那个上下文。
  • 父元素上新加的 opacity、transform 或 filter 突然把它的下拉菜单困住了。
  • 使用小数。z-index: 1.5 是无效的。
  • 以为 z-index: 0 和 auto 表现相同。0 会创建层叠上下文;auto 不会。
  • 模态框放在嵌套很深的组件里。把它们渲染在 <body> 的末尾附近(或使用 <dialog> 元素的顶层),这样就没有祖先上下文能困住它们。

常见问题

z-index 有什么作用?

它设置重叠元素的层叠顺序:在同一个层叠上下文中,值较大的元素绘制在值较小的元素前面。默认值是 auto,取值为整数,也可以是负数。

为什么我的 z-index 不起作用?

要么元素没有定位(z-index 需要 position 不是 static,或者元素是 flex 或 grid 的子项),要么它位于一个层级较低的、形成了层叠上下文的父元素里。如果子元素所在的层叠上下文被放在某个元素下面,那么无论子元素自己的 z-index 是多少,都无法超过那个元素。

什么是层叠上下文?

一组作为一个整体层叠在一起的元素。在它内部,子元素按各自的 z-index 排序;在它外部,整组元素按创建它的那个元素的 z-index 放置。

什么会创建层叠上下文?

包括但不限于:根元素、z-index 不为 auto 的定位元素、position: fixed 或 sticky、设置了 z-index 的 flex 或 grid 子项、小于 1 的 opacity、transform、filter、isolation: isolate,以及作用于这些属性的 will-change。

没有 position,z-index 还有效吗?

对普通的块元素或行内元素无效:position: static 会忽略它。但对 flex 项目和 grid 项目,不设置任何 position 也有效。

Coddy 编程语言插图

和 Coddy 一起学编程

开始