3 深入理解CSS层叠

 

一、前言

css中,层叠概念颇为重要,对相关概念的理解透彻,在发现问题、分析问题、解决问题的过程中会起到至关重要的作用。

下面将梳理一些重要概念:

  • 文档流(Normal Flow)
  • 格式化上下文(Formatting Context)
  • 三维空间
  • 层叠上下文(Stacking Context)
  • 层叠水平(Stacking Level)
  • 层叠顺序 (Stacking Order)

二、文档流

CSS中,文档流是一个很基础也是很重要的一个概念。很多时候她被称为Document Flow,但在CSS的标准被称为Normal Flow,即普通流常规流。大家更喜欢称之为文档流。那么CSS的文档流是怎么一回事呢?

HTML中任何一个元素其实就是一个对象,也是一个盒子。在默认情况下它是按照出现的先后顺序来排列,而这个排列的顺序就是文档流。

文档流是元素在Web页面上的一种呈现方式。所有的HTML元素都是块盒子(Block Boxes,块级元素)或行内框(Inline Boxes,行内元素)。当浏览器开始渲染HTML文档时,它从窗口的顶端开始,经过整个文档内容的过程中,分配元素需要的空间。除非文档的尺寸被CSS规则限定,否则浏览器垂直扩展文档来容纳全部的内容。每个新的块级元素渲染为新行。行内元素则按照顺序被水平渲染直到当前行遇到边界,然后换到下一行垂直渲染。

 

如果你读过CSS相关的规范,不难发现这样的过程包括了块格式化(BFC:Block formatting context)、行内格式化(IFC:Inline formatting context)、相对定位(Relative positioning)和Run-in Boxes的定位。

事实上,在普通文档流中的盒子属于一种格式化上下文(Formatting Context),大家较为熟悉的就是块格式化上下文(Block formatting context)和行内格式化上下文(Inline formatting context)。不过有一点面要注意,它们只能是其中一者,不能同时属于两者。言外之意,任何被渲染的HTML元素都是一个盒子(Box),这些盒子不是块盒子就是行内盒子。即使是未被任何元素包裹的文本,根据不同的情况,也会属于匿名的块盒子或行内盒子。

综合上面的描述,也可以理解格式化上下文对元素盒子做了一定的范围的限制,其实就是类似有一个widthheight做了限制一样。如果从这方面来理解的话,普通流就是这样的一个过程:

  • 在对应的块格式化上下文中,块级元素按照其在HTML源码中出现的顺序,在其容器盒子里从左上角开始,从上到下垂直地依次分配空间层叠(Stack),并且独占一行,边界紧贴父盒子边缘。两相邻元素间的距离由margin属性决定,在同一个块格式化上下文中的垂直边界将被重叠(Collapse margins)。除非创建一个新的块格式化上下文,否则块级元素的宽度不受浮动元素的影响。
  • 在对应的行内格式化上下文中,行内元素从容器的顶端开始,一个接一个地水平排列。

 

扯了这么多,如果简单的描述就是:如何排列HTML元素而已。拿个块格式化上下文的普通文档流来举例,就像下面这样:

<div>1</div> <div>2</div><div>3</div><div>4</div><div>5</div>

对应的效果如下:

 

 

上例中看到的文档流就是一个普通的文档流,也是一个正常的普通文档流。

CSS中也可以通过float或者position:absolute两种方法让元素脱离文档流。而这两者的表现实际上非常相似。简单的可以理解为部分无视完全无视的区别:

使用float脱离文档流时,其他盒子会无视这个元素,但其他盒子内的文本依然会为这个元素让出位置,环绕在周围(可以说是部分无视)。而对于使用position:absolute脱离文档流的元素,其他盒子与其他盒子内的文本都会无视它(可以说是完全无视)。

 

 

三、格式化上下文

在介绍文档流一节中,多次提到了格式化上下文概念。那么格式化上下文指的是什么呢?

格式化上下文指的是初始元素定义的环境。

其中主要包括两个要点,一个是元素定义的环境,另一个是初始化

css中,元素定义的环境有两种,也就是前面提到的:块格式化上下文行内格式化上下文。这两种上下文定义了在css中元素所处的环境,格式化则表明了在这个环境中,元素处理此环境中应当被初始化。用一句话来描述就是:

元素在此环境中应当如何排版布局等。

 

块格式化上下文其实也是大家常常称为的BFC,指的是Web页面的可视化CSS渲染的一部分,是布局过程中生成块级盒子的区域,也是浮动元素与其他元素的交互限定区域。

下列方式会创建块格式化上下文:

 

  • 根元素或包含根元素的元素
  • 浮动元素(元素的 float 不是 none
  • 绝对定位元素(元素的 position 为 absolute 或 fixed
  • 行内块元素(元素的 display 为 inline-block
  • 表格单元格(元素的 display table-cellHTML表格单元格默认为该值)
  • 表格标题(元素的 display 为 table-captionHTML表格标题默认为该值)
  • 匿名表格单元格元素(元素的 display tabletable-row table-row-grouptable-header-grouptable-footer-group(分别是HTMLtablerowtbodytheadtfoot的默认属性)或 inline-table
  • overflow 值不为 visible 的块元素
  • display 值为 flow-root 的元素
  • contain 值为 layoutcontent strict 的元素
  • 弹性元素(display flex 或 inline-flex元素的直接子元素)
  • 网格元素(display grid 或 inline-grid 元素的直接子元素)
  • 多列容器(元素的 column-count 或 column-width 不为 auto,包括 column-count 为 1)
  • column-span  all 的元素始终会创建一个新的BFC,即使该元素没有包裹在一个多列容器中(标准变更,Chrome bug)。

 

创建了块格式化上下文的元素中的所有内容都会被包含在该BFC中。

关于BFC的相关概念和应用可以查看对BFC的理解及应用。

相对于格式化上下文,在行内格式化上下文中,盒子一个接一个地水平排列,起点是包含块的顶部。水平方向上的margin,borderpadding在盒子之间得到保留。盒子在垂直方向上可以以不同的方式对齐:它们的顶部或底部对齐,或根据其中文字的基线对齐。包含那些框的长方形区域,会形成一行,叫做行框

CSS中,对于行框这样的东东涉及的页就更多了。这里不做过多的阐述。感兴趣的同学,自己可以阅读相关规范深究。

 

 

四、三维空间

平时我们从设备终端看到的HTML文档都是一个平面的,事实上HTML文档中的元素却是存在于三个维度中。除了大家熟悉的平面画布中的x轴和y轴,还有控制第三维度的z轴。

 

 

其中x轴通常用来表示水平位置,y轴来表示垂直位置,z轴表示屏幕内外方向上的位置。

 

 

对于xy轴我们很易于理解,一个向右,一个向下。但对于z轴,理解起来就较为费力。在CSS中要确定沿着z轴排列元素,表示的是用户与屏幕的这条看不见的垂直线:

 

 

从正常流的一节中我们可以知道,如果元素不脱离文档流,或者不通过其他CSS的规则来改变初始化的格式化上下文环境,元素盒子是不可能会有层叠在一起的。但我们使用floatposition:absolute时可以让元素脱离文档流。那么问题来了:

当一个设置了z-index值的定位元素与常规文档流中的元素相互重叠的时候,谁会被置于上方?

当定位元素与浮动元素相互重叠的时候,谁会被置于上方?

当定位元素被嵌套在其他定位元素中时会发生什么?

要回答这些问题,我们需要进一步地理解z-index是如何工作的,尤其是层叠上下文,以及层叠次序这些概念。

五、层叠上下文 (Stacking Context)

上一节提到过,网页及其每个元素都有一个坐标系统。该系统包括一个三维z轴,其中的元素是层叠(Stacked)的。z轴的方向指向查看者,x轴指向屏幕的右边,y轴指向屏幕的底部。

 

 

 

通常,浏览器会按照CSS规范中指定的特定顺序放置元素:

DOM树中最先出现的元素被放在首位,之后出现的元素被放在前面的元素之上。但它并不总是那么简单。只有当页面上的所有元素是自然流才起作用。也就是说,当没有元素在流中的位置被改变或者已经脱离文档流,才起作用。

 

CSS中有两种方式影响元素的流和位置的方法:

  • 使用position属性定位元素。除了默认的static值外的元素被称为定位元素
  • 通过使用float属性浮动元素来改变元素的流

事实上,每个HTML元素都属于一个层叠上下文。给定层叠上下文中的每个定位元素都具有一个整数的层叠层级,具有更大堆栈级别的元素盒子总是在具有较低堆栈级别的盒子的前面(上面)。盒子可能具有负层叠级别。层叠上下文中具有相同堆栈级别的框根据文档树出现的顺序层叠在一起。

文档中的层叠上下文由满足以下任意一个条件的元素形成:

  • 根元素(HTML)
  • z-index值不为autoposition值为absolute | relative
  • position 值为 fixedsticky
  • z-index 值不为 auto  Flex 的元素,即:父元素 display: flex|inline-flex
  • opacity 属性值小于 的元素
  • transform 属性值不为 none的元素
  • mix-blend-mode 属性值不为 normal 的元素
  • filterperspectiveclip-pathmaskmask-imagemask-bordermotion-path 值不为 none 的元素
  • perspective 值不为none 的元素
  • isolation 属性被设置为 isolate 的元素
  •  will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值
  • -webkit-overflow-scrolling 属性被设置 touch的元素

而且每个网页都有一个默认的层叠上下文。这个层叠上下文的根源就是html元素。html元素中的一切都被置于这个默认的层叠上下文的一个层叠层上。理解起来有点怪。那么先来看一个图:

 

 

层叠上下文1 (Stacking Context 1)是由文档根元素形成的。 层叠上下文2和3 (Stacking Context 2, 3) 都是层叠上下文1 (Stacking Context 1) 上的层叠层。 他们各自也都形成了新的层叠上下文,其中包含着新的层叠层。

总结:

  • 层叠上下文可以包含在其他层叠上下文中,并且一起组件了一个有层级的层叠上下文
  • 每个层叠上下文完全独立于它的兄弟元素,当处理层叠时只考虑子元素,这里类似BFC
  • 每个层叠上下文是自包含的:当元素的内容发生层叠后,整个该元素将会在父级层叠上下文中按顺序层叠

六、层叠水平(Stacking Level)

层叠水平即层叠等级,决定了同一个层叠上下文中元素在z轴上的显示顺序:

  • 普通元素的层叠等级优先由其所在的层叠上下文决定
  • 层叠等级的比较只有在同一个层叠上下文元素中才有意义
  • 在同一个层叠上下文中,层叠等级描述定义的是该层叠上下文中的元素在Z轴上的上下顺序

注意,层叠等级并不一定由 z-index 决定,只有定位元素的层叠等级才由 z-index 决定,其他类型元素的层叠等级由层叠顺序、他们在HTML中出现的顺序、他们的父级以上元素的层叠等级一同决定,详细的规则见下面层叠顺序的介绍。

 

七、层叠顺序 (Stacking Order)

HTML文档中,默认情况之下有一个自然层叠顺序(Natural Stacing Order),即元素在z轴上的顺序。它是由许多因素决定的。在CSS2.1的年代,在CSS3还没有出现的时候(注意这里的前提),层叠顺序规则遵循下面这个列表,它显示了元素盒子放入层叠顺序上下文的顺序,从层叠的底部开始,共有七种层叠等级:

  • 背景和边框:形成层叠上下文的元素的背景和边框。 层叠上下文中的最低等级。
  • z-index值:层叠上下文内有着负z-index值的子元素。
  • 块级盒:文档流中非行内非定位子元素。
  • 浮动盒:非定位浮动元素。
  • 行内盒:文档流中行内级别非定位子元素,即inlineinline-block
  • z-index: 0z-index:auto:定位元素。 这些元素形成了新的层叠上下文。
  • z-index值:定位元素。 层叠上下文中的最高等级。

用图表示对应如下:

 

 

这七个层叠等级构成了层叠次序的规则。 在层叠等级七上的元素会比在等级一至六上的元素显示地更上方(更靠近观察者)。 可以结合w3help中的一张图来帮助我们更好的理解这七个层叠等级:

 

 

 

posted @ 2019-07-05 14:15  一阙梅曲香素笺  阅读(427)  评论(0编辑  收藏  举报