理解CSS中的层叠上下文

740 阅读6分钟

层叠上下文(stacking context)

前言

关于层叠上下文这篇文章,本次当做学习记录,大部分信息来源其他文章,如果有错,感谢纠正。主要是复习记录,但例子和图片是自己画和写的。

概念

默认情况下,CSS是流式布局的,元素与元素之间不会重叠。流式布局的意思可以理解:在一个矩形的水面上,放置很多矩形的浮块,浮块会漂浮在水面上,且彼此之间依次排列,不会重叠在一起。

这是要绘制它们其实非常简单,一个个按顺序绘制即可。不过有些情况下,这种流式布局会被打破,比如使用了浮动(float)和定位(position)。因此需要需要识别出哪些脱离了正常文档流的元素,并记住它们的层叠信息,以便正确地渲染它们。那些脱离正常文档流的元素会形成一个层叠上下文,可以将层叠上下文简单理解为一个个的薄层(类似Photoshop的图层),薄层中有很多DOM元素,这些薄层叠在一起,最终形成了我们看到的多彩的页面。

层叠上下文是HTML中的一个三维概念。在css2.1的规范中,每个元素都是三维的,分别平铺在画布上的X轴,Y轴,Z轴,一般情况下,元素在页面上沿着x,y轴平铺,用户察觉不到它们在z轴上的层叠关系。一旦元元素发生层叠,这时它可能覆盖了其他元素或者被其他元素覆盖。如果一个元素含有层叠上下文,那么这个元素则为层叠上下文元素。我们可以理解为这个元素比普通元素在z轴上距离用户更近一些。

规则

  • 层叠上下文的层叠水平比普通元素要高
  • 层叠上下文可以嵌套,内部层叠上下文及其子元素的层叠水平受制于外部的层叠上下文
  • 层叠上下文和其兄弟元素相互独立,挤在不同的层叠上下文中,元素的层叠顺序没有可比性;
  • 不同的层叠上下文元素发生层叠时,元素的层叠水平由父元素的层叠上下文的层叠顺序决定

形成

形成层叠上下文的方法有

  1. 根元素
  2. position值为absolute【绝对定位】,relative【相对定位】,且z-index不为auto的元素
  1. position值为fixed【固定定位】,sticky【粘滞定位】
  2. z-index值不为auto的flex元素;即:父元素为display:flex||inline-flex
  1. grid容器的子元素,且z-index值不为auto
  2. opacity属性小于1的元素
  1. mix-blend-mode属性值不为normal的元素
  2. perspective/transform/filter,perspective,clip-path,mask,mask-image,mask-border,motion-path值不为none的元素
  1. isolation属性被设置为isolate的元素
  2. will-change中指定了任意css属性,即使你没有直接指定这些属性的值,该属性在 non-initial 值时会创建层叠上下文的元素
  1. -webkit-overflow-scrolling属性被设置touch的元素
  2. contain 属性值为 layout、paint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。

等级

在同一层叠上下文中,定义的是该层叠上下文元素在z轴上的上下顺序。层叠等级的比较只有在同一个层叠上下文元素中才有意义,在同一个层叠上下文中,层叠等级描述定义的是该层叠上下文中的元素在Z轴上的上下顺序。

顺序

指元素遵循的规则,在同一个层叠上下文中,其中的元素遵循以下规则:

背景和边框:形成层叠上下文的背景和边框。

负z-index值:层叠上下文内有着-index值的定位子元素,负得愈大层叠等级越低;

块级盒:文档流中块级,非定位子元素;

浮动盒:非定位浮动元素;

行内盒:文档流中行内,非定位子元素;

z-index:0:z-index为0或auto的定位元素,这些元素形成了新的层叠上下文;

+z-index:z-index为正的定位元素,正的越大层叠等级越高;

对于处于同一层的元素,它们的堆叠顺序由元素在文档中出现的顺序决定。

详细的规则在w3官方文档中有描述,大家可以参考:
www.w3.org/TR/css-posi…

比较规则:

  1. 不同层叠上下文的元素,比较他们所在层叠上下文的层级,随所在层叠上下文整体进行排列。

  2. 同一个层叠上下文中,按照层叠顺序,层叠层级越高的元素越z轴上面。

  3. 当层叠层级和层叠顺序都相同,在DOM流中处于后面的元素会覆盖前面的元素。

  4. 元素层叠顺序只能够在自身所处的层叠上下文中比较 如果要与其他上下文进行比较的话 就是比较父元素。

  5. 如果一个元素有多个层级的元素,选择它的最高层进行比较。

  • 为什么定位元素会层叠在普通元素的上面

其根本原因就在于,元素一旦成为定位元素,其z-index就会自动生效,此时其z-index就是默认的auto,也就是0级别,根据上面的层叠顺序表,就会覆盖inline或block或float元素。

而不支持z-index的层叠上下文元素天然z-index:auto级别,也就意味着,层叠上下文元素和定位元素是一个层叠顺序的,于是当他们发生层叠的时候,在DOM流中处于后面的元素会覆盖前面的元素。

举例说明

1.普通情况

<div class="box1" style="width: 100px;height: 100px;background-color: brown;">
    <div class="red">box1-1</div>
    <div class="green">box1-2</div>
</div>
<div class="box2" style="width: 100px;height: 100px;background-color:#f0f">
    <div class="blue">box2-1</div>
    <div class="blue">box2-2</div>
</div>

在这个例子中,形成的层叠上下文就只有html,box1与box2按照出现顺序排列。

2.相同层级

<div class="box1" style="width: 400px;height:100px;position: fixed;z-index:1;background-color: brown;">
</div>
<div class="box2" style="width: 50px;height:100px;position: fixed;z-index:1;background-color: rgb(190, 170, 170);">
</div>

box1与box2都会形成了新的层叠上下文,并且他们的层级相同,并且他们的都在html这一个层叠上下文中,所以层叠关系由他们代码的顺序确定,越后出现层级越高,在最上层。

2.父子元素

  <div class="box1" style="width: 200px;position: absolute;z-index:-1;height: 300px;background-color: brown;">
            <div style="width: 500px;height:200px;position: absolute;z-index:-10;background-color: rgb(141, 121, 121);">box1-1</div>
</div>

box1形成了一个层叠上下文,位于倒数第二层,但是box1的背景处于倒数第一层,子元素的层叠上下文z-inex为负,处于倒数第二层,子层叠上下文的层叠顺序在父层叠上下文之上。他们是父子层叠关系,不是兄弟层叠上下文,所以不能通过z-index来比较他们的层叠等级。

如果想让子元素在父元素之下,将父元素不产生层叠上下文,并且让子元素z-index为负即可。

<div class="box1" style="width: 200px;height: 300px;background-color: brown;">
  <div style="width: 500px;height:200px;position: absolute;z-index:-10;background-color: rgb(141, 121, 121);">box1-1</div>
</div>

2.在相同层叠上下文的父元素内的情况

<div class="box1" style="width: 200px;position: absolute;z-index:-1;height: 300px;background-color: brown;">
     <div class="box1-1" style="width: 500px;height:200px;position: absolute;z-index:9;background-color: rgb(141, 121, 121);">box1-1</div>
</div>
<div class="box2" style="width: 100px;position: absolute;height: 100px;background-color:#f0f">
     <div class="box2-1" style="position: absolute;z-index:9;background-color: rgb(73, 32, 138);width:500px;">box2-2</div>
</div>

在这个例子中,box1与box2分别形成了两个层叠上下文,box1:z-index为负,box2:z-index为正;

所以box2>box1层级;而在box1子中,box1的层叠上下文的背景始终为负一层,<box1-1的z-index为负的层级;

同理 box2中box1的层叠上下文的背景始终为负一层<box2-2的z-index=9

所以就是box1<box1-1<box2<box2-2

z-index

z-index只适用于定位的元素,对非定位元素无效,它可以被设置为正整数,负整数,0,auto,如果一个定位元素没有设置z-index,那么默认为auto;元素的z-index值只在同一层叠上下文有意义。

如果父级层叠上下文的层叠等级低于另一个层叠上下文的,那么它z-index设得再高也没有用。所以如果你遇到z-index设了很大,但是不起作用的话,就去看看它的父级层叠上下文是否被其他层叠上下文盖住了。

参考:

www.infoq.cn/article/2ka…

segmentfault.com/a/119000001…

应用: html2canvas