视觉格式化模型
写于2016年04月19日

我们可以把网页中的每个元素看作一个盒,所谓的页面的布局就是把每个盒按照一定规则排列起来最终形成页面。

CSS 视觉格式化模型的一部分工作是从文档元素生成盒。生成盒拥有不同类型,并对视觉格式化模型的处理产生影响。生成盒的类型取决于 CSS 属性。

一些通用概念

包含块(containing blocks)

  • 许多盒子的位置和大小都是依据一个名为包含块的盒子确定的
  • 一般来说生成盒会作为器后代的包含块,我们称盒其后代”创建”了包含块
  • 我们所说的”一个盒的包含块“指的是它所处的包含块,而不是指它生成的包含块

    原文引用

    The phrase “a box’s containg block” means “the containing block in which the box lives” not the one it generates

替换元素

替换元素是指那些元素本身会被其他内容替换掉的元素。比如说image元素,它在渲染时会被图片替换掉。canvas在渲染时会被一个画布替换掉。video元素会被一个视频播放器替换掉。

块的布局方式

我们刚学习CSS的时候,大部分教程上面会说CSS有两种类型的元素。一种是块级元素,比如divp等等。还有一种是内联元素,比如spana等等。两种元素的布局方式是不一样的,我们先看一下块级元素的布局方式。

块的相关概念

  • 块级元素(block-level element)的概念:常见的块级元素有divh123p等等。更准确的描述是display属性为blocklist-itemtable的元素。块级元素视觉上呈现为块,竖直排列。
  • 块级盒(block-level box)的概念:块级盒用于描述它与父元素和同级元素之间的视觉表现。每个块级元素都会生成一个至少一个块级盒(li元素会生成两个),但是每个元素都只会生成一个主要块级盒(principal block-level box)用于包含其子元素盒生成的内容。任何定位方案都与这个主要块级盒有关。块级盒会参与块格式化上下文(block formatting context)(即传说中的BFC)
  • 块容器盒(block container box)的概念:块容器盒用于描述跟它后代之间的视觉表现。除了table元素创建的盒和可替换元素(比如canvasvideo),其他任意元素生成的块级盒也是一个块容器盒,这个盒也称为块盒。一个块容器盒要么只包含块级盒,要么创建一个行格式化上下文(后面回谈论到)从而只包含行内级盒。
    下图三个概念之间的关系表示:

匿名块盒(Anonymous block boxes)

块容器盒要么只包含行内级盒(后面讨论行内联相关的概念),要么只包含块级盒(block-level box)。但通常情况下会包含两者,这个时候就会创建匿名块盒,让我们看看下面的代码。

1
2
3
4
5
<div>
Some inline text
<p>followed by a paragraph</p>
followed by more inline text.
</div>

  1. 首先是最外层的div元素,是一个块级元素,会创建一个块级盒。同时这个块级盒也是一个块容器盒,会包含元素的内容。
  2. 中间的p元素也是个块级元素,所以p元素会创建一个块级盒。
  3. p元素前后有两个没有任何元素包裹的文字。由于我们刚才说过,块容器盒要么只包含块级盒,要么只包含行内级盒,由于我们这里的p元素已经生成了一个块级盒,所以p前后两句的两句文字会生成两个匿名块盒包含他们。
  4. 匿名块盒不能被选中设置样式,它们只能从父级元素继承样式。
    最终生成的盒如下图所示

块格式化上下文(block formatting context)

简称BFC,BFC就是规定块盒在页面中如何布局,可以是CSS布局的地基,并非什么很特殊的概念。BFC有以下规定:

  • 块级盒一个一个从上往下垂直排列
  • 块级盒垂直方向的边距距离由margin决定。属于同一个BFC的两个相邻的块级盒的margin会发生重叠
  • 每个元素的margin box的左边与包含块的border box的左边相接触,同样对于右边也是。
  • BFC的区域不会与float box重叠(利用这个特性,我们可以做自适应元素大小)
  • BFC就是页面上的一个隔离的独立容器,容器里面的元素布局不会影响到容器外的元素。
  • 计算BFC的高度时,浮动元素也参与计算(清除浮动的原理)
  • 以下元素会产生BFC
    • 根元素
    • float属性不为none的元素
    • positionabsolute或者fixed
    • displayinline-blocktable-celltable-captionflexinline-flex的元素
    • overflow不为visible

内联元素的布局方式

前面我们说了块级元素的布局方式,现在我们来看看内联元素比如spana的布局方式。

内联的相关概念

  • 行内级元素(inline-level element):当元素的display属性为inlineinline-blockinline-table时,它就是行内级元素
  • 行内级盒(inline-level box):行内级元素会生成行内级盒,它参与行内格式化上下文。同时行内级盒还有行内盒原子行内级盒两种,我们在下面解释。
  • 行内盒(inline box):如果行内级盒的内容会参与盒本身的行内格式化上下文,那么这个行内级盒也称为行内盒。display:inline的非替换元素生成的盒就是行内盒。
  • 原子行内级盒(atomic inline-level box)的概念:如果行内级盒的内容本身不参与盒本身的行内格式化上下文,那么这些行内级盒就称为原子行内级。可替换行内元素(比如image)或displayinline-blockinline-table生成的行内级盒就是原子行内级盒。
    上面这些概念关系如下图:
  • 行盒(line boxes):能把同一行上的行内级盒都包含进去的矩形区域称为行盒,行盒由行内格式化上下文(inline formatting context)产生。在块盒里面,行盒从块盒的一边排版到另一边。当有浮动元素时,行盒从向左浮动元素的右边缘排版到向右浮动元素的左边缘。
  • 匿名行内盒(anonymous inline boxes):当文字没有被行内元素包裹时,就会为这段文字生成匿名行内盒。这种行为类似于匿名块盒。

行内格式化上下文(inline formatting context)

简称IFC,IFC规定了行内级盒该按照什么规则排列。当下面这些情况出现时,会产生IFC:

  • 块容器盒中只有行内级元素时会产生IFC
  • 匿名行内盒中会建立IFC
    ·
    IFC包含以下规定:
  • 行盒的宽度是由包含块盒存在的浮动元素来决定的,一般来说行盒的宽度就是包含块的宽度,但是浮动框可以插在行盒和包含块之间,因此行盒的宽度可能由于 浮动造成的可用空间会变小 而有所改变。
  • 行盒一定会高到足以容纳所有盒,但也可能比它所包含的最高框还要搞(比如这些框是由基线对齐)。
  • 当一个盒的高度小于包含它的行盒的高度,它在行盒中的垂直位置由vertical-align属性决定。后面详细说明高度的计算方式。
  • 当几个行内级盒在水平方向上无法塞进同一个行盒里面时,它们会被拆分后分布在两个或者多个行盒中。行盒之间会以没有垂直间距也没有重叠的方式堆叠起来。
  • 当一个行内级盒的总宽度小于包含他们的行盒的宽度,则他们在行盒的水平分布由text-align属性来决定,如果该属性为justify,则用户代理可以拉伸行内级盒中的空格和字间距(该盒不能是原子行内级盒)。
  • 当一个行内级盒的宽度超过了行框的宽度,那么它会被分成几个盒,这几个盒会分布在几个行盒,如果该行内级盒不可分割(当个字符、或语言指定的文字打断规则不允许出现打断时),那么该行内级盒会溢出行盒。原子行内级盒不允许分割。
  • 行内级盒被分割的时候,外边距、边框和内边距在出现分割的地方都没有视觉效果
  • 行盒如果同时满足以下条件,会以零高度来对待
    • 不包含文本或保留空格
    • 不包含内外边距或边框宽度非零的行内元素
    • 不包含其他正常排版内容(imageinline-blockinline-table
    • 不以保留的换行符结尾