用了那么久的 CSS ,但你还是不懂它

前段时间有人问:为什么我设置的 z-index 值不生效呢?

我说:那可能是你真的不知道 CSS 层叠上下文。

图片

但是话说回来,都用了那么久 CSS 了,这都不知道是不是有些说不过去呀,这也是本文产生的主要原因,希望对你有所帮助!

本文就以 CSS 层叠上下文 和 图层 Layers 为主,来探讨一下那些不被你重视的 CSS 基本知识。

你可能奇怪那什么会涉及到 图层 呢?

因为怕你可能会 搞混、分不清 呗,带来的后果就是你在做语言表达时就会互相混用概念!

图片

CSS 层/堆 叠上下文

先看示例(修炼假秘籍)

先不着急讲概念,来看看平时你最容易犯错的栗子!
图片

float 元素

这里简单介绍一下 float,因为我相信很多人并不是那么清楚它的规则(即使很简单),我们都知道 float 浮动元素 可以覆盖 block 块元素,例如:
<body>
<div class=”float-box”>float box</div> // float: left
<div class=”div-box”>div box</div>
</body>
图片
那么 调换 一下这两个元素的 顺序 又如何,如下:
<body>
<div class=”div-box”>div box</div>
<div class=”float-box”>float box</div> // float: left
</body>
图片
【扩展】这 浮动元素 怎么就不覆盖 块元素 了?
很简单了,这是因为 浮动元素 是有 定位规则 的:
  • 当一个元素产生 浮动 后,它会被移出 正常文档流,要么 向左 或 向右 平移
  • 一直平移直到碰到了所处的 容器边框,或碰到 另外一个浮动元素 为止
图片

float 元素 和 inline 元素

上面的表现结果很明显了,float 元素 可以覆盖 block 元素,那么换成 inline 行内元素 又如何,直接借助 第一个示例的图片,其中的文本就可以当做 inline 元素:
图片
不难发现,其中的 文本 没有被 浮动元素 覆盖(实际上把文字部分换成 <img> 也是一样的),按道理要文本的父元素都被覆盖了,文本不应该不被覆盖,这时候有人可能就会出来回答了:float 元素 允许 文本 和 内联元素 环绕它。
说得对(你的学秘籍是这样告诉你的),但是这里实际上是涉及到了 CSS 层叠上下文。
图片

 

为什么需要 CSS 层叠上下文?

我们最常见页面布局其实就是沿着 x 轴 和 y 轴 排列的,换句话说就是 从左到右(行内元素、行内块元素)、从上到下(块元素) 的方式排列,但是在某些场合下 元素和元素之间是需要发生重叠 的,例如:定位元素、margin 等属性设定了 负值 时。
既然元素存在可能重叠的情况,那么就需要为其制定规则,否则 不同浏览器 怎么知道该如何重叠呢?
因此,就需要 CSS 层叠上下文 的概念,同时因为是涉及到层叠顺序,那就不再是简单的按照 x 轴 和 y 轴 方向排列,而是引入了虚构的 z 轴,优先级越高的元素渲染时在 z 轴 上就会处于 更高层,离用户视角更近。
图片

 

什么是 CSS 层叠上下文?

假定用户正面向(浏览器)视窗或网页,而 HTML 元素沿着其相对于用户的一条虚构的 z 轴 排开,层叠上下文 就是对这些 HTML 元素的一个 三维构想,多个 HTML 元素基于其 元素属性 按照 优先级顺序 占据这个空间。
图片
以上是 官方 说法,大白话 如下:
顾名思义,层叠上下文 中 层叠 这两个字应该不难理解吧,那么既然是层叠的,自然就需要分 先后顺序,而这个 优先级 是根据某些 CSS 属性 来制定的,而所谓的 上下文 就是这个规则作用的内容区。
图片

如何生成 CSS 层叠上下文?

文档中的层叠上下文由 满足以下任意一个条件 的元素形成:
  • 文档根元素(<html>)
  • position 值为 absolute(绝对定位)或 relative(相对定位),且 z-index 值不为 auto 的元素
  • position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素
  • flex 容器的 子元素,且 z-index 值不为 auto
  • grid 容器的 子元素,且 z-index 值不为 auto
  • opacity 属性值小于 1 的元素
  • transform 属性不为 none 的元素
  • will-change 值设定任一 CSS 属性 的元素
以上只是列举了部分常见的,更多可见 MDN 层叠上下文
图片

值得注意的是:

  • 层叠上下文 可 包含 在 其他层叠上下文 中,并共同创建一个层叠上下文层级
  • 【大白话】:一个 父层叠上下文 中可以包含多个 子层叠上下文,且它们都所属于 父层叠上下文,而不是生成和 父层叠上下文 同级的 新层叠上下文
  • 每个层叠上下文都 完全独立 于它的兄弟元素
  • 当处理层叠时只考虑子元素
  • 【大白话】:对外部而言,父叠上下文 不论包含了多少 子层叠上下文 都会作为 整体 按优先级渲染;对内部而言,父叠上下文 中的多个 子层叠上下文 还是会按优先级渲染
  • 每个层叠上下文都是 自包含 的
  • 当一个元素的内容发生层叠后,该元素将被 作为整体 在父级层叠上下文中按顺序进行层叠
  • 【大白话】:一个元素变成 层叠元素 后,其与其子元素 就会与其他 兄弟层叠元素 按优先级进行渲染
CSS 层叠上下文中的渲染顺序(优先级)
详细、最新的 规则在 W3 文档 —— Elaborate description of Stacking Contexts 中有完整说明,虽然描述的很详细,但是估摸着会吓退一部分人,这里简单总结一下。
图片

层叠上下文 中 子元素 的 绘制顺序 规则如下,每个元素的渲染基本遵循 背景色 — 背景图(如果有) — 边框(如果有) 的顺序:

  • 元素为 根元素(HTML)
  • 背景色
  • 背景图
  • 元素为 z-index < 0 的 定位元素(position)
  • 元素为 (非定位)块元素(block)
  • 非 table 元素
  • 背景色
  • 背景图
  • 边框
  • table 元素
  • <table> 背景色
  • <table> 背景图
  • <table> 列 背景色、背景图
  • <table> 行 背景色、背景图
  • <table> 单元格 背景色、背景图
  • <table> 边框
  • 元素为 (非定位)浮动元素(float)
  • 元素为 (非定位)内联(块)元素(inline/inline-block)
  • 元素为 z-index = 0 | auto 的 定位元素(position)
  • 元素为 z-index >= 1 的 定位元素(position)
图片
其实也就是对应下面这张大家最常见的 CSS 层叠上下文的 7 层简化图,没有一开始就贴出来是因为图中的描述过于简洁了,你必须要对原本的规则比较熟悉才能套用起来,不然结果就是 你以为的真的只是你以为的 :
图片

 

如何确定两个元素的层叠顺序?

要比较两个元素的层叠顺序,我们先要确定它们 所在的层叠上下文 的层叠顺序:

  • 若两个元素处于 同一个层叠上下文 中,那么就按同一层叠上下文中的渲染顺序来计算
  • 若两个元素处于 不同层叠上下文 中,那么它们 所在的层叠上下文的顺序 就决定了目标元素的层叠顺序
    • 假设 A 层叠上下文 的层级高于 B 层叠上下文,那么 A 中的子元素 都会高于 B 中的子元素,此时对 B 中的子元素 设置多大的 z-index 都没有用,因为 z-index 只能控制 同一层叠上下文中元素的 堆叠顺序
    • 假设 A 层叠上下文 和 B 层叠上下文 层级相同,那么 后面的层叠上下文 就会覆盖 前面的层叠上下文
图片
【扩展】 规则 + 特性 = 结果
列举出上面的 7 层简化图 后,有部分同学可能会存有疑问,那就是下图中的 div box 文本 按道理应该会覆盖到 float box 元素 上才对呀,但这也不是覆盖啊!
图片
因为 规则(层叠上下文规则) + 特性(元素特性) = 结果(视图) ,虽然 CSS 层叠上下文 的规则是 float < inline(即 文本 应该覆盖 浮动元素),但是别忘了 浮动元素的特性:
图片
所以 文本 不会真的 “覆盖” 在 浮动元素 上,而是 环绕在其周围,只不过本文着重讲的是 CSS 层叠上下文 而已。

再看示例(修炼真秘籍)

示例一:利用 CSS 层叠上下文不让 浮动元素 覆盖 块元素

正常情况下,浮动元素 和 块级元素 之间的表现如下:
图片
那么现在我们也可以通过层叠上下文来 解释覆盖现象:
在 同一个层叠上下文 中渲染的 优先级 为 float 元素 > block 元素 ,因此在渲染时就需要保证 float 元素 在三维的 Z 轴 上要离用户更近
那么如何利用 层叠上下文 让 float 元素 不覆盖 block 元素 呢?
那就是让 block 元素 产生一个新的层叠上下文呗,例如将它的 opacity 属性值
设置成任意 小于 1 的值即可,以下是设置了 opacity: 0.9; 的效果:
图片
如果你还没想到,那么可以请自己喝一杯 QQㄋㄟㄋㄟ好喝到咩噗茶 !
图片

示例二:z-index 申请出战

在你的印象中 定位元素 是不是一定会覆盖其他 非定位元素 呀,来看看是不是:
<style>
 .abs-box{
    background-color: red;
    position: absolute;
  }
  .div-box {
    background-color: rgb(16, 4, 181);
  }
</style>
  
<body>
  <div class="box abs-box">absolute box</div>
  <div class="box div-box">div box</div>
</body>
图片
好像没问题的喔 ~ ~ ~
同上一个示例,我们给 .div-box 元素设置一个 opacity: 0.9; 再看看:
图片
没问题,按之前的理解此时 .div-box 元素 产生了 新的层叠上下文,所以自然就覆盖了 .abs-box 元素,如果你之前看得足够认真,那么你一定会发现此时 .abs-box 元素并没有产生 层叠上下文,如果有疑问可以回头再看看。
此时我们因为没有为其设置 z-index 属性,即默认就为 z-index:auto; 不满足生成条件,那么我们可以设置 z-index: 0 让其满足生成 层叠上下文 的条件,不过即使这样设置了也和上图是一样的。
图片
首先 z-index 只针对 position 定位元素 有效,其规定了元素在 z 轴 的位置,每个层都有一个顺序数,顺序数大的层在上面,小的在下面,其默认就是在 第 0 层 渲染。
于是乎,为了解决上面的 position 元素 被覆盖的问题,我们就可以为其设置 z-index: 1 即可:
图片

 

图层 Layer
前面已经讲了为什么要谈到图层,因为很多人会误以为 CSS 层叠上下文 这种元素渲染相互 覆盖 的现象直接给归类为不同的 图层,但实际上这两者完全是不一样的,当然这里也不会去讲什么底层原理,尽量简洁。
图片

什么是图层?

这里的 图层 Layer 实际上就是,浏览器 解析、渲染 视图过程中的一个环节(老八股):
  • 构建 DOM Tree
  • 构建 CSSOM
  • 构建 Render Tree
  • 构建 Layout Tree
  • 构建 Layer Tree(划分图层)
  • 根据视口大小对 图层 进行 分块
  • 栅格化 raster
  • 合成 和 显示
划分图层的 目的 就是为了实现通过 GPU 计算来加速渲染 实现 硬件加速。
图形渲染的 必要条件 包括 复杂的数学 和 几何计算,而 GPU 就是专门为了这些复杂计算而被设计的,通过划分不同的图层,计算它们变动的频率,再决定交由 CPU 渲染 还是 GPU 渲染 来实现优化。
图片

什么样的元素会创建新图层?

这里不卖关子了,直接列举一些常见的条件,如下:
图片

怎么观察图层?

那当然是用浏览器的 Devtools 去观察了, 而 Devtools 又分为 Safari Devtools 和 Chrome Devtools,而关于其中的具体比较可见 神光大佬 的 什么 css 会新建图层?别猜了,Devtools 都写了。

Safari Devtools

图片

Chrome Devtools

图片

最后
小小的总结一下:
  • 一个元素产生层叠上下文,那么它会比其他元素拥有高优先级
  • 一个层叠上下文中元素的排列顺序:
  • 层叠上下文的 背景色、背景图(如果有)、边框(如果有)
  • 定位元素,且 z-index < 0
  • (非定位)块元素
  • (非定位)浮动元素
  • (非定位)行内(块)元素
  • 定位元素,且 z-index = auto | 0
  • 定位元素,且 z-index > 0
  • 比较两个元素的层叠顺序,先确定它们 所处的层叠上下文 的层叠顺序
  • 处于 相同层叠上下文,则按如上顺序计算
  • 处于 不同层叠上下文,则比较它们各自层叠上下文的顺序
  • 层叠上下文 和 图层 不是同一个东西,即使从视图表现来看是相似的,但本质不同
好了,以上就是本文的全部内容,希望你不要在混淆这两个概念而傻傻分不清楚了,也希望能够让你修炼到 真秘籍,而不是那些被生搬硬套的 假秘籍。
希望本文对你有所帮助!!!
扫码领红包

微信赞赏支付宝扫码领红包

发表回复

后才能评论