在 CSS 中我们广泛地使用两种“盒子” —— 块级盒子 (block box) 和 内联盒子 (inline box)。这两种盒子会在页面流(page flow)和元素之间的关系方面表现出不同的行为:
- 一个被定义成块级的(block)盒子会表现出以下行为:
- 盒子会在内联的方向上扩展并占据父容器在该方向上的所有可用空间,在绝大数情况下意味着盒子会和父容器一样宽
- 每个盒子都会换行
- width 和 height 属性可以发挥作用
- 内边距(padding), 外边距(margin)和 边框(border)会将其他元素从当前盒子周围“推开”
- 如果一个盒子对外显示为 inline,那么他的行为如下:
- 盒子不会产生换行。
- width 和 height 属性将不起作用。
- 垂直方向的内边距、外边距以及边框会被应用但是不会把其他处于 inline 状态的盒子推开。
- 水平方向的内边距、外边距以及边框会被应用且会把其他处于 inline 状态的盒子推开。
在 CSS 中,所有的元素都被一个个的“盒子(box)”包围着,理解这些“盒子”的基本原理,是我们使用 CSS 实现准确布局、处理元素排列的关键。
视觉格式化模型
CSS 视觉格式化模型(visual formatting model)是用来处理和在视觉媒体上显示文档时使用的计算规则。视觉格式化模型会根据CSS 盒子模型将文档中的元素转换为一个个盒子,每个盒子的布局由以下因素决定:
- 盒子的尺寸:精确指定、由约束条件指定或没有指定
- 盒子的类型:行内盒子(inline)、行内级盒子(inline-level)、原子行内级盒子(atomic inline-level)、块盒子(block)
- 定位方案(positioning scheme):普通流定位、浮动定位或绝对定位
- 文档树中的其他元素:即当前盒子的子元素或兄弟元素
- 视口尺寸与位置
- 所包含的图片的尺寸
- 其他的某些外部因素
该模型会根据盒子的包含块(containing block)的边界来渲染盒子。通常,盒子会创建一个包含其后代元素的包含块,但是盒子并不由包含块所限制,当盒子的布局跑到包含块的外面时称为溢出(overflow)。
盒模型
当对一个文档进行布局(layout)的时候,浏览器的渲染引擎会根据标准之一的 CSS 基础框盒模型(CSS basic box model),将所有元素表示为一个个矩形的盒子(box)。CSS 决定这些盒子的大小、位置以及属性。
每个盒子由四个部分(或称区域)组成,其效用由它们各自的边界(Edge)所定义。与盒子的四个组成区域相对应,每个盒子有四个边界:内容边界 Content edge、内边距边界 Padding Edge、边框边界 Border Edge、外边框边界 Margin Edge。
- 标准盒模型 在标准模型中,如果你给盒设置 width 和 height,实际设置的是 content box。padding 和 border 再加上设置的宽高一起决定整个盒子的大小。
- 替代(IE)盒模型 默认浏览器会使用标准模型。如果需要使用替代模型,您可以通过为其设置 box-sizing: border-box 来实现。这样就可以告诉浏览器使用 border-box 来定义区域,从而设定您想要的大小。
包含块
一个元素的尺寸和位置经常受其包含块(containing block)的影响。大多数情况下,包含块就是这个元素最近的祖先块元素的内容区域,但也不是总是这样。确定一个元素的包含块的过程完全依赖于这个元素的 position 属性:
- 如果 position 属性为 static、relative 或 sticky,包含块可能由它的最近的祖先块元素(比如说 inline-block, block 或 list-item 元素)的内容区的边缘组成,也可能会建立格式化上下文 (比如说 a table container, flex container, grid container, 或者是 the block container 自身)。
- 如果 position 属性为 absolute ,包含块就是由它的最近的 position 的值不是 static (也就是值为fixed, absolute, relative 或 sticky)的祖先元素的内边距区的边缘组成。
- 如果 position 属性是 fixed,在连续媒体的情况下 (continuous media) 包含块是 viewport ,在分页媒体 (paged media) 下的情况下包含块是分页区域 (page area)。
- 如果 position 属性是 absolute 或 fixed,包含块也可能是由满足以下条件的最近父级元素的内边距区的边缘组成的:
- transform 或 perspective 的值不是 none
- will-change 的值是 transform 或 perspective
- filter 的值不是 none 或 will-change 的值是 filter(只在 Firefox 下生效)。
- contain 的值是 paint
- backdrop-filter 的值不是 none
盒子的生成
盒子的生成是 CSS 视觉格式化模型的一部分,用于从文档元素生成盒子。盒子有不同的类型,不同类型的盒子的格式化方法也有所不同。盒子的类型取决于 CSS display 属性。
块级元素与块盒子
- 块级元素
- 当元素的 display 为 block、list-item 或 table 时,该元素将成为块级元素。一个块级元素会被格式化成一个块,默认按照垂直方向依次排列。
- 块盒子
- 每个块级盒子都会参与块格式化上下文(block formatting context)的创建,而每个块级元素都会至少生成一个块级盒子,即主块级盒子(principal block-level box)。
- 主块级盒子包含由后代元素生成的盒子以及内容,同时它也会参与定位方案。
- 一个块级盒子可能也是一个块容器盒子。块容器盒子(block container box)要么只包含其他块级盒子,要么只包含行内盒子并同时创建一个行内格式化上下文(inline formatting context)。
- 一个同时是块容器盒子的块级盒子称为块盒子(block box)。
- 匿名块盒子
- 在某些情况下进行视觉格式化时,需要添加一些增补性的盒子,这些盒子不能用 CSS 选择符选中,因此称为匿名盒子(anonymous boxes)。
- CSS 选择器不能作用于匿名盒子 (anonymous boxes),所以它不能被样式表赋予样式。也就是说,此时所有可继承的 CSS 属性值都为 inherit ,而所有不可继承的 CSS 属性值都为 initial。
- 块包含盒子可能只包含行内级盒子,也可能只包含块级盒子,但通常的文档都会同时包含两者,在这种情况下,就会在相邻的行内级盒子外创建匿名块盒子。
行内级元素和行内盒子
- 行内级元素
- 如果一个元素的 display 属性为 inline、inline-block 或 inline-table,则称该元素为行内级元素。显示时,它不会生成内容块,但是可以与其他行内级内容一起显示为多行。
- 行内盒子
- 行内级元素会生成行内级盒子,该盒子同时会参与行内格式化上下文(inline formatting context)的创建。行内盒子既是行内级盒子,也是一个其内容会参与创建其容器的行内格式化上下文的盒子,比如所有具有 display:inline 样式的非替换盒子。
- 匿名行内盒子
- 类似于块盒子,CSS 引擎有时候也会自动创建一些行内盒子。这些行内盒子无法被选择符选中,因此是匿名的,它们从父元素那里继承那些可继承的属性,其他属性保持默认值 initial。
- 一种常见的情况是 CSS 引擎会自动为直接包含在块盒子中的文本创建一个行内格式化上下文,在这种情况下,这些文本会被一个足够大的匿名行内盒子所包含。但是如果仅包含空格则有可能不会生成匿名行内盒子,因为空格有可能会由于 white-space 的设置而被移除,从而导致最终的实际内容为空。
其他类型的盒子
- 行盒子
- 行盒子由行内格式化上下文创建,用来显示一行文本。在块盒子内部,行盒子总是从块盒子的一边延伸到另一边。当有浮动元素时,行盒子会从向左浮动的元素的右边缘延伸到向右浮动的元素的左边缘。
- Run-in 盒子
- Run-in 盒子通过 display:run-in 来定义,它可以是块盒子,也可以是行内盒子,这取决于紧随其后的盒子的类型。Run-in 盒子可以用来在可能的情况下将标题嵌入文章的第一个段落中。
- 由其他模型引入的盒子
- 表格内容模型 (en-US)可能会创建一个表格包装器盒子和一个表格盒子,以及多个其他盒子如表格标题盒子等
- 多列内容模型 (en-US)可能会在容器盒子和内容之间创建多个列盒子
- 实验性的网格内容模型或 flex-box 内容模型同样会创建一些其他种类的盒子
区块格式化上下文
区块格式化上下文(Block Formatting Context,BFC)是 Web 页面的可视 CSS 渲染的一部分,是块级盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。下列方式会创建块格式化上下文:
- 文档的根元素(
<html>)。 - 浮动元素(即 float 值不为 none 的元素)。
- 绝对定位元素(position 值为 absolute 或 fixed 的元素)。
- 行内块元素(display 值为 inline-block 的元素)。
- 表格单元格(display 值为 table-cell,HTML 表格单元格默认值)。
- 表格标题(display 值为 table-caption,HTML 表格标题默认值)。
- 匿名表格单元格元素(display 值为 table(HTML 表格默认值)、table-row(表格行默认值)、table-row-group(表格体默认值)、table-header-group(表格头部默认值)、table-footer-group(表格尾部默认值)或 inline-table)。
- overflow 值不为 visible 或 clip 的块级元素。
- display 值为 flow-root 的元素。
- contain 值为 layout、content 或 paint 的元素。
- 弹性元素(display 值为 flex 或 inline-flex 元素的直接子元素),如果它们本身既不是弹性、网格也不是表格容器。
- 网格元素(display 值为 grid 或 inline-grid 元素的直接子元素),如果它们本身既不是弹性、网格也不是表格容器。
- 多列容器(column-count 或 column-width 值不为 auto,且含有 column-count: 1 的元素)。
- column-span 值为 all 的元素始终会创建一个新的格式化上下文,即使该元素没有包裹在一个多列容器中。
格式化上下文影响布局,通常,我们会为定位和清除浮动创建新的 BFC,而不是更改布局,因为它将:
- 包含内部浮动。
- 排除外部浮动。
- 阻止外边距重叠。
外边距折叠
区块的上下外边距有时会合并(折叠)为单个边距,其大小为两个边距中的最大值(或如果它们相等,则仅为其中一个),这种行为称为外边距折叠。注意:有设定浮动和绝对定位的元素不会发生外边距折叠。有三种情况会形成外边距折叠:
- 相邻的兄弟元素:相邻的同级元素之间的外边距会被折叠(除非后面的元素需要清除之前的浮动)。
- 没有内容将父元素和后代元素分开:如果没有设定边框(border)、内边距(padding)、行级(inline)内容,也没有创建区块格式化上下文或间隙来分隔块级元素的上边距(margin-top)与其内一个或多个子代块级元素的上边距(margin-top);或者没有设定边框、内边距、行级内容、高度(height)或最小高度(min-height)来分隔块级元素的下边距(margin-bottom)与其内部的一个或多个后代后代块元素的下边距(margin-bottom),则会出现这些外边距的折叠,重叠部分最终会溢出到父代元素的外面。
- 空的区块:如果块级元素没有设定边框、内边距、行级内容、高度(height)、最小高度(min-height)来分隔块级元素的上边距(margin-top)及其下边距(margin-bottom),则会出现其上下外边距的折叠。
相关术语
- 块:block,一个抽象的概念,一个块在文档流上占据一个独立的区域,块与块之间在垂直方向上按照顺序依次堆叠。
- 包含块:containing block,包含其他盒子的块称为包含块。
- 盒子:box,一个抽象的概念,由 CSS 引擎根据文档中的内容所创建,主要用于文档元素的定位、布局和格式化等用途。盒子与元素并不是一一对应的,有时多个元素会合并生成一个盒子,有时一个元素会生成多个盒子(如匿名盒子)。
- 块级元素:block-level element,元素的 display 为 block、list-item、table 时,该元素将成为块级元素。元素是否是块级元素仅是元素本身的属性,并不直接用于格式化上下文的创建或布局。
- 块级盒子:block-level box,由块级元素生成。一个块级元素至少会生成一个块级盒子,但也有可能生成多个(例如列表项元素)。
- 块盒子:block box,如果一个块级盒子同时也是一个块容器盒子,则称其为块盒子。除具名块盒子之外,还有一类块盒子是匿名的,称为匿名块盒子(Anonymous block box),匿名盒子无法被 CSS 选择符选中。
- 块容器盒子:block container box 或 block containing box,块容器盒子侧重于当前盒子作为“容器”的这一角色,它不参与当前块的布局和定位,它所描述的仅仅是当前盒子与其后代之间的关系。换句话说,块容器盒子主要用于确定其子元素的定位、布局等。
盒子分为“块盒子”和“块级盒子”两种,但元素只有“块级元素”,而没有“块元素”。
- 行内级元素:inline-level element,display 为 inline、inline-block、inline-table 的元素称为行内级元素。与块级元素一样,元素是否是行内级元素仅是元素本身的属性,并不直接用于格式化上下文的创建或布局。
- 行内级盒子:inline-level box,由行内级元素生成。行内级盒子包括行内盒子和原子行内级盒子两种,区别在于该盒子是否参与行内格式化上下文的创建。
- 行内盒子:inline box,参与行内格式化上下文创建的行内级盒子称为行内盒子。与块盒子类似,行内盒子也分为具名行内盒子和匿名行内盒子(anonymous inline box)两种。
- 原子行内级盒子:atomic inline-level box,不参与行内格式化上下文创建的行内级盒子。原子行内级盒子一开始叫做原子行内盒子(atomic inline box),后被修正。原子行内级盒子的内容不会拆分成多行显示。