简介
**块格式化上下文(Block Formatting Context,BFC)**
是 Web 页面的可视化 CSS 渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。
注意:一个 BFC 的范围包含
创建该上下文元素的所有子元素
,但不包括创建了新 BFC 的子元素的内部元素
。这从另一方角度说明,一个元素不能同时存在于两个 BFC 中
。因为如果一个元素能够同时处于两个 BFC
中,那么就意味着这个元素能与两个 BFC 中的元素发生作用,就违反了 BFC 的隔离作用。
下列方式会创建块格式上下文:
- 根元素或包含根元素的元素
- 浮动元素(元素的
float
不是none
) - 绝对定位元素(元素的
position
为absolute
或fixed
) - 行内块元素(元素的
display
为inline-block
) - 表格单元格(元素的
display
为table-cell
,HTML 表格单元格默认为该值) - 表格标题(元素的
display
为 table-caption
,HTML 表格标题默认为该值) - 匿名表格单元格元素(元素的
display
为table
、table-row
、table-row-group
、table-header-group
、table-footer-group
(分别是HTML table
、row
、tbody
、thead
、tfoot
的默认属性)或inline-table
) overflow
值不为visible
的块元素display
值为flow-root
的元素contain
值为layout
、content
或strict
的元素- 弹性元素(
display
为flex
或inline-flex
元素的直接子元素) - 网格元素(
display
为grid
或inline-grid
元素的直接子元素) - 多列容器(元素的
column-count
或column-width
不为auto
,包括column-count
为 1) column-span
为 all 的元素始终会创建一个新的BFC
,即使该元素没有包裹在一个多列容器中(标准变更,Chrome bug)。
块格式化上下文包含创建它的元素内部的所有内容。
表现
在BFC(Block formatting contexts)中,在包含块内一个盒子一个盒子不重叠地垂直排列,两个兄弟盒子直接的垂直距离由 margin
决定。浮动也是如此(虽然有可能两个盒子的距离会因为 floats 而变小),除非该盒子再创建一个新的BFC
。
三中文档流的定位方案
我们常说的文档流其实分为定位流、浮动流、普通流三种。而普通流其实就是指BFC
中的FC
。FC(Formatting Context)
,直译过来是格式化上下文,它是页面中的一块渲染区域,有一套渲染规则,决定了其子元素如何布局,以及和其他元素之间的关系和作用。常见的FC
有BFC(Block Formatting Contexts)、IFC
,还有GFC(GridLayout Formatting Contexts)
和FFC(Flex Formatting Contexts)
。
大致的文档流分类
- 普通文档流
(Normal flow)
- 浮动
(Floats)
- 绝对定位
(Absolute positioning)
普通文档流
- 在普通文档流中,盒一个接着一个排列;
- 在块级格式化上下文里面, 它们竖着排列;
- 在行内格式化上下文里面, 它们横着排列;
- 当 position 为 static 或 relative,并且 float 为 none 时会触发普通文档流;
- 对于静态定位(static positioning),position: static,盒的位置是普通文档流布局里的位置;
- 对于相对定位(relative positioning),position: relative,盒偏移位置由 top、bottom、left、right 属性定义。即使有偏移,仍然保留原有的位置,其它普通文档流不能占用这个位置。
浮动(Floats)
- 左浮动元素尽量靠左、靠上,右浮动同理
- 这导致普通文档流环绕在它的周边,除非设置 clear 属性
- 浮动元素不会影响块级元素的布局
- 但浮动元素会影响行内元素的布局,让其围绕在自己周围,撑大父级元素,从而间接影响块级元素布局
- 最高点不会超过当前行的最高点、它前面的浮动元素的最高点
- 不超过它的包含块,除非元素本身已经比包含块更宽
- 行内元素出现在左浮动元素的右边和右浮动元素的左边,左浮动元素的左边和右浮动元素的右边是不会摆放浮动元素的
绝对定位(Absolute positioning)
- 绝对定位方案,盒从普通文档流中被移除,不影响普通文档流的布局;
- 它的定位相对于它的包含块,相关 CSS 属性:top、bottom、left、right;
- 如果元素的属性 position 为 absolute 或 fixed,它是绝对定位元素;
- 对于 position: absolute,元素定位将相对于上级元素中最近的一个 relative、fixed、absolute,如果没有则相对于 body;
BFC 大致规则
- 内部的 Box 会在垂直方向上一个接一个的放置
- 内部的 Box 垂直方向上的距离由 margin 决定。(完整的说法是:属于同一个 BFC 的两个相邻 Box 的 margin 会发生折叠,不同 BFC 不会发生折叠。)
- 每个元素的左外边距与包含块的左边界相接触(从左向右),即使浮动元素也是如此。(这说明 BFC 中子元素不会超出他的包含块,而 position 为 absolute 的元素可以超出他的包含块边界)
- BFC 的区域不会与 float 的元素区域重叠
- 计算 BFC 的高度时,浮动子元素也参与计算
普通文档流布局
- 浮动的元素是不会被父级计算高度
- 非浮动元素会覆盖浮动元素的位置
- margin 会传递给父级元素
- 两个相邻元素上下的 margin 会重叠
开发中的使用
- BFC 可以防止 margin 折叠
- 可以阻止元素被浮动元素覆盖
- 多列布局中使用 BFC
- 可以包含浮动元素
BFC 可以防止 margin 折叠
了解边距合并是另一个被低估的 CSS
技能。在下一个示例中,假设有一个背景颜色为灰色的 div
。
这个 div 包含两个标签 p。外部 div 元素的 margin-bottom 为 40
像素,标签 p 的顶部和底部 margin 都是 20 像素。
1 | <div class="outer"> |
1 | .outer { |
效果如下图所示:
可以看到它和外层div
的margin-bottom
也有重叠,两个相邻的 p 段落的margin
也有重叠。
在 CSS 当中,相邻的两个盒子(可能是兄弟关系也可能是祖先关系)的外边距可以结合成一个单独的外边距。这种合并外边距的方式被称为折叠,并且因而所结合成的外边距称为折叠外边距。折叠的结果按照如下规则计算:
- 两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值。
- 两个相邻的外边距都是负数时,折叠结果是两者绝对值的较大值。
- 两个外边距一正一负时,折叠结果是两者的相加的和。
产生折叠的必备条件:margin 必须是邻接的!
如果我们把盒子设为 BFC,它现在包含了标签 p
和它们的边距,这样它们就不会折叠,我们可以看到边距后面容器的灰色背景。
修改 css 代码如下:
1 | .outer { |
效果图如下:
可以阻止元素被浮动元素覆盖、可以包含浮动元素
你将熟悉 BFC 的这种行为,因为使用浮动的任何列类型布局都是这样工作的。如果一个项目创建了一个 BFC,那么该项目将不会包裹任何浮动元素。在下面的例子中,代码如下:
1 | <div class="outer"> |
1 | .outer { |
效果如下图所示:
我可以通过将包裹文本的 div 设置为 BFC 来防止这种包裹行为。新增 css 代码如下:
1 | .text { |
这实际上是我们创建具有多个列的浮动布局的方法。浮动项还为该项创建了一个 BFC,因此,如果右边的列比左边的列高,那么我们的列就不会相互环绕。
在多列布局中使用 BFC
如果我们创建一个占满整个容器宽度的多列布局,在某些浏览器中最后一列有时候会掉到下一行。这可能是因为浏览器四舍五入了列宽从而所有列的总宽度会超出容器。但如果我们在多列布局中的最后一列里创建一个新的 BFC,它将总是占据其他列先占位完毕后剩下的空间。
代码如下:
1 | <div class="container"> |
1 | .column { |
效果如下:
添加以下样式创建一个 BFC:
1 | .column:last-child { |