本文是阅读张鑫旭老师《CSS 世界》这本书的学习笔记。《CSS 世界》花了一章的篇幅,介绍网页元素的层叠规则。我看过两遍了,但还是记不住。于是又看了一遍,记了这个笔记,用我自己的话表述了一下。文中还附有我写的 demo 链接,贴在这里,跟大家分享。

Photo by Henry Lim on Unsplash
平时开发里会用到 z-index 属性,用来提升元素在页面中的层级,不被其他元素覆盖。举一些场景的例子:比如吸顶导航、自定义下拉框、自定义 tooltip、侧边悬浮对话框啥的。
z-index 的适应范围
z-index 的值是个整数,可以是正数,也可以是个负数,值越大,层级越高。但 z-index 是有使用范围的,它只在定位元素和 Flex 项目上使用才有效。
- 所谓“定位元素”就是
position值不为static的元素。 - Flex 项目是对 Flex 容器(即
display值为flex或inline-flex)直接子元素的称谓。
层叠顺序
z-index 的默认值是 auto,层叠效果等同于 z-index: 0。它的层叠水平是介于正数与负数之间的。
类似下面这样:

demo 地址:codepen.io/zhangbao/pe…
可以看见普通元素的层级是夹在 负 z-index 和 z-index: 0 之间的。普通元素按照层叠从小到大的顺序可以再细分为:块状元素、浮动元素和 inline 水平盒子。
因此,我们可以得到如下更加细节的顺序图:

demo 地址:codepen.io/zhangbao/pe…
可见,块状元素、浮动元素和 inline 水平盒子的层叠水平是依次增加的。
注意,这里的“inline 水平盒子”是指
inline/inline-block/inlint-table元素。
从中我们也能看到,层叠水平不是仅仅适用于使用了 z-index 值的定位元素/Flex 项目,普通元素也具备层叠水平——即,所有元素都具有层叠水平。只不过应为用惯了 z-index 属性控制元素层级,感觉元素的层叠水平,只跟 z-index 属性有关系一样,这是不对的。
层叠规则
上面讲的是不同层叠水平元素的层叠顺序。那么,如果元素的层叠水平是一致的,那么又遵循什么规则呢?
有两个规则:
- 后来居上
- 谁大谁上
先讲 后来居上。举个例子:
<div style="display: flex;">
<div>Flex 项目 1</div>
<div>Flex 项目 2</div>
</div>
效果:

demo 地址:codepen.io/zhangbao/pe…
我们已经知道 Flex 项目的 z-index 默认值是 auto,层叠效果等同于 z-index: 0。因为现在 DOM 结构上靠后,因此比第一个 Flex 项目的层级要高。
再讲 谁大谁上。
前面的 层叠顺序图 其实已经在表达这个意思了,但还不够明显。还是以上面 demo 为例,我们显式指定两个 Flex 项目的 z-index 值,就能看出区别了。
<div style="display: flex;">
<div style="z-index: 1;">Flex 项目 1</div>
<div style="z-index: 0;">Flex 项目 2</div>
</div>

1 比 0 大,所以第一个 Flex 项目在第二个的上面了。
但 z-index 并不能使元素上天入地无所不能,因为它还要受到 层叠上下文 这个“结界”的限制。
什么是层叠上下文?
层叠上下文可理解为一个“结界”,自成一个小世界。这个小世界中,可能存在其它的“结界”,而自身也可能处于其他“结界”之中。
可以通过以下方式创建一个层叠上下文:
- 页面根元素
<html>。成为根层叠上下文。 z-index值为数值的定位元素(这里有个特例:在 Chrome、Safair 等 Webkit 内核的浏览器,position: fixed元素天然是层叠上下文元素,无需z-index为数值)。- Flex 项目(父元素
display: flex|inline-flex),同时z-index值不是auto。 - 元素的
opacity值不是none,并且要小于1。 - 元素的
transform值不是none。 - 元素的
mix-blend-mode值不是normal。 - 元素的
filter值不是none。 - 元素的
isolation值是isolate。 - 元素的
will-change属性值是上面 4~8 中的任意一个(比如will-change: opacity、will-change: transform)。 - 元素的
-webkit-overflow-scrolling设为touch。
其中,从第 4~10 项的元素称为“不依赖 z-index 的层叠上下文”,这些元素天然是 z-index: auto 级别。因此,我们的层叠顺序图,可以进一步补充为:

这里添加了一个“不依赖 z-index 的层叠上下文”,与之前的“z-index: auto 或看成 z-index: 0”(适应于定位元素和 Flex 项目)在同一个层叠水平。
demo 地址:codepen.io/zhangbao/pe…
我们通过里两个小例子,来说明层叠上下文的“结界”作用。
两个小例子
例子一:
<div class="p" style="position: relative; z-index: auto;">
<div class="c" style="z-index: 2;">z-index: 2</div>
</div>
<div class="p" style="position: relative;z-index: auto;">
<div class="c" style="z-index: 1;">z-index: 1</div>
</div>
效果:

通过上面对层叠上下文的定义,我们知道 z-index: auto; 的 position: relative 元素不是层叠上下文。根据 谁大谁上 的层叠规则,z-index: 2 在 z-index: 1 之上。
我们稍微修改下代码:
<div class="p" style="position: relative; z-index: 0;">
<div class="c" class="box" style="z-index: 2;">z-index: 2</div>
</div>
<div class="p" style="position: relative;z-index: 0;">
<div class="c" class="box" style="z-index: 1;">z-index: 1</div>
</div>
效果:

demo 地址:codepen.io/zhangbao/pe…
根据定义,z-index 值为数值的定位元素具有层叠上下文。就是说,这个案例里的层叠规则,不是简单的比较两个 .c 的 z-index 值了,因为引入了层叠上下文,那么 .c 的层叠关系就是取决于父元素 .p 之间的关系了。
层叠上下文元素天然就是 z-index: auto 级别的,对于同层叠水平的两个 .p,根据 后来居上 的层叠规则,后一个 .p 的层级更高, 因此最终的表现是:z-index: 2 只能屈居于 z-index: 1 之下了。
层叠顺序(完整版)
既然层叠上下文是个“结界”,那么无论层叠上下文中所有后代元素的层叠顺序如何,都不会超出这个“结界”。
现在可以告诉大家的是,这个结界的“底儿”就是它的 background 和 border。请看下面的图:

demo 地址:codepen.io/zhangbao/pe…
这就是完整版的层叠顺序图,层叠上下文的 background 和 border 是整个顺序图的最底层。
以上 CSS 世界的层叠规则的所有内容啦!
(正文完)
广告时间(长期有效)
我有一位好朋友开了一间猫舍,在此帮她宣传一下。现在猫舍里养的都是布偶猫。如果你也是个爱猫人士并且有需要的话,不妨扫一扫她的【闲鱼】二维码。不买也不要紧,看看也行。
(完)