深刻理解文档流(Document Flow)和视觉格式化模型(CSS Visual Formatting Model)

前言

最开始学习CSS的时候,我一度认为CSS基础很简单,可是CSS布局是一块难啃的骨头。又到后来,我去亚马逊买了畅销书HTML and CSS, design and build websites,书上内容浅显易懂,可是关于CSS布局,也就只有一章,并且彷佛也没有提到复杂的文档流,我印象中也并不知道'BFC', 'Stacking Contexts'这些名词。css

最近的复习中不断出现'负margin','BFC','Stacking Contexts'等等概念,又在MDN上了解到CSS Visual Formatting Model,我以为有必要系统地梳理一遍思路。html

本文以MDN Visual Formatting Model 这篇文章做为引导,并加入本身的理解和新增的知识点,不会涉及太多的布局真实场景,例如双飞翼布局,圣杯布局,多列等高布局等等,这里只做为基础。web

CSS标准盒子模型(Box Model)

CSS标准盒子模型是呈现每个独立元素的基础,组合起来构成文档流ide

如下是一个标准的盒子,由4个部分组成布局

标准盒模型咱们设置width属性的时候,width = content-width,怪异盒模型width = content-width + padding-width + border-widthpost

当咱们设置背景色或者背景图片时,默认会延伸到border外围,可是Z-Ordering的顺序在border如下,也就是说,若是同时设置了backgroundborder属性,border区域仍是显示border,可是其Z-Order如下是background。咱们可使用background-clip属性改变这一默认行为,具体再也不叙述。学习

block元素和inline元素造成盒子的区别

<p>test</p>
<p>test</p>
<a href="">test</a>
<a href="">test</a><br />
<a href="">test</a>
<a href="">test</a>
复制代码
p, a {
  width: 100px;
  height: 40px;
  border: 1px solid red;
  margin: 20px;
  padding: 30px;
}
复制代码

图一

图二

仔细看上面两幅图,块级和行级元素盒子有如下几点不一样flex

  1. 块级元素盒子会自动占满一行,行级元素盒子不会
  2. 块级元素能够设置宽高,行级元素宽高设置无效
  3. 块级元素之间上下margin有折叠,行级元素设置上下margin无效,可是左右margin有效,并且不会折叠
  4. 行级元素content部分出如今正常文档流中,设置上下padding会与其余元素发生重合,左右padding不会

CSS容器盒子(Containing Block)

仅仅了解完每一个元素本身造成的盒子模型是不够的,由于自身的盒子在某些时候还会受到容器盒子(Containing Block)的影响,例如ui

  1. 基于百分比的值来设定元素盒子的width,height,margin,padding,这里须要注意,除了height是基于父元素盒子的height以外,其他三个属性都是基于父元素盒子的width
  2. 当设置为Absolute Positioning以后,基于百分比的值来设置盒子的偏移量,例如top,left等等,此时是基于父元素盒子border内侧进行定位

关于容器盒子,咱们最常认为它就是父元素的content部分,其实否则,有以下三种状况spa

  1. 若是子元素position属性为默认值static或者是relative,则其Containing Block为父元素的content部分
  2. 若是子元素position属性为absolute,则其Containing Block为第一个position不为static的父元素的content+padding部分,若是都是static,则为Initial Containing Block

Initial Containing Block

The initial containing block has the dimensions of the viewport, and is also the block that contains the htmlelement. Simply put, the absolutely positioned element will be contained outside of the html element, and be positioned relative to the initial viewport.

简而言之,就是视窗(Viewport)

  1. 若是子元素position属性为fixed,则其Containing Block为视窗(Viewport)

知道标准盒子模型以及容器盒子模型后,须要用必定的规则将这些盒子“组装”起来,如下咱们来看看“组装”方式

正常流(Normal Flow)

一旦盒子模型建立成功,接下来即是将这些盒子按照必定规则组装起来,默认的组装规则就是Normal Flow

使用position: relative或者默认的position: static,且没有设定浮动,此时元素就是在Normal Flow中

Normal Flow中,块级元素盒子垂直方向一个挨着一个,行级元素盒子水平方向一个挨着一个,一行不够以后换到下一行

若是使用position: relative,能够将盒子基于原来的位置,经过设定top,bottom,left,right进行位移

浮动流(Floats)

设置一个元素为浮动以后,这个元素盒子在一行中移动到最左边或者最右边,而且脱离Normal Flow

浮动元素对其后面的元素或者是其父元素都会产生影响,除非其后元素使用clear属性清除这一影响,我以前写过一篇从圣杯和双飞翼看浮动流的文章,里面详细解释了浮动流的过程。

绝对定位(Absolute Positioning)

在Floats中,虽然盒子脱离了文档流,可是其对于以后的元素盒子或者是父元素盒子都会产生影响,除非影响被清除。可是在Absolute Positioning Scheme中,盒子能够说彻底抽离Normal Flow,且再也不对其余元素产生影响

此时元素盒子的位置彻底基于其容器盒子(Containing Block),主要两种状况

  1. 若是绝对定位使用position: absolute,容器盒子为第一个position不为static的父元素的content+padding部分
  2. 若是绝对定位使用position: fixed,容器盒子为视窗(Viewport)

堆叠顺序(Z-Ordering)

前面提到的都是元素应该出如今文档中的位置,可是若是发生堆叠,那么显示顺序(Z-Ordering, or Stacking Ordering)是怎么样的呢?

不使用z-index

HTML文档默认显示给咱们的是第0层,这也是z-index的默认值,为0

  • bottom layer (farthest from the observer)
  • ...
  • Layer -3
  • Layer -2
  • Layer -1
  • Layer 0 (default rendering layer)
  • Layer 1
  • Layer 2
  • Layer 3
  • ...
  • top layer (closest to the observer)

正常的堆叠顺序,遵循以下几个规则(顺序由底向上,若是发生重叠,只有最上的元素能被用户看到)

  1. 根元素(<html>)的backgroundborder,在堆底
  2. 没有被定位的元素(position: static),根据在HTML文档出现顺序,后面出现的在堆顶
  3. 被定位的元素(除了static),根据在HTML文档出现顺序,后面出现的在堆顶
  4. 若是使用display: flex,且对flex container下的子元素使用order属性改变顺序,也会影响到堆叠顺序,order最高的元素出如今堆顶

依据上面的规则,咱们看一个例子

5个DIV在文档的出现顺序为1,2,3,4,5

使用z-index

首先来看看z-index的定义

The z-index CSS property specifies the z-order of a positioned element and its descendants or flex items (children of an element with display: flex). When elements overlap, z-order determines which one covers the other. An element with a larger z-index generally covers an element with a lower one.

也就是说,z-index只能在flex-item或者positioned-items下设置才有效,不然无效

再看一个例子(在上一个例子基础上使用z-index

5个DIV在文档的出现顺序为1,2,3,4,5

Stacking Context

其实咱们使用z-index,其实是造成了一个Stacking Context,不一样的Stacking Context下的元素层叠顺序互不影响,它们之间的层叠顺序由上层元素之间的层叠顺序决定

建立Stacking Context有以下几种常见方法

  1. HTML文档根元素<html>建立了默认的Stacking Context
  2. 设置了position: fixed或者absolute的元素,且z-index值不为默认值auto
  3. 设置了display: flexcontainer下的子元素,且z-index值不为默认值auto
  4. 设置opacity值小于1的元素

使用z-index改变堆叠顺序

前面提到过,不一样的Stacking Context下的元素层叠顺序互不影响,它们之间的层叠顺序由上层元素之间的层叠顺序决定

z-index的initial(默认值)为auto

auto

The box does not establish a new local stacking context. The stack level of the generated box in the current stacking context is the same as its parent's box.

也就是说,若是一个元素没有建立Stacking Context,则其顺序由父级Stacking Context决定,最顶层的Stacking Context为根元素<html>建立,其z-index值为0

仍是先看一个例子,下面分别是文档结构和结果图

Root

  • DIV #1
  • DIV #2
  • DIV #3
    • DIV #4
    • DIV #5
    • DIV #6

总结

到这里Document Flow和CSS Formatting Model也就结束了,涵盖了基本的布局规则。只有深刻理解这些基础,才能更好地去理解之前的一些布局Trick,好比双飞翼布局,圣杯布局等等,这些布局创建在这些基础上,若是理解了这些内容,本身实现一些布局Trick也不是不可能,虽然如今Flex布局已经能够解决不少问题了

参考

  1. developer.mozilla.org/en-US/docs/…
  2. developer.mozilla.org/en-US/docs/…
相关文章
相关标签/搜索