这几天研究了 W3C 的官方文档,想寻找些思路,提高一下 CSS 水平,先从 BFC 说起吧:
常规流
常规流中的盒属于一个格式化上下文,可能是块或是内联,但不能都是(既是块又是内联)。块级盒参与块格式化上下文,内联级盒参与内联格式化上下文
本文讨论 BFC (块级格式化上下文),暂不讨论 IFC (内联格式化上下文)
块级格式化上下文
BFC 被新建立,满足下列任一条件即可:
-
浮动元素(确切的说除了
none以外的值) -
绝对定位元素
-
非块盒的块容器(
inline-block,table-cell,table-caption) -
overflow不为visible的块盒
在一个块格式化上下文中,盒在竖直方向一个接一个地放置,从包含块的顶部开始。两个兄弟盒之间的竖直距离由
margin属性决定。同一个块格式化上下文中的相邻块级盒之间的竖直margin会合并
在一个块格式化上下文中,每个盒的
left外边挨着包含块的left边(对于从右向左的格式化,right边挨着)。即使存在浮动(尽管一个盒的行盒可能会因为浮动收缩),这也成立。除非该盒建立了一个新的块格式化上下文(这种情况下,该盒自身可能会因为浮动变窄)
BFC的应用
解决外边距叠加
那么,什么是外边距叠加呢?
在CSS中,两个或两个以上的盒子的外边距(可能是也可能不是相邻元素)可能结合成单个的外边距。这种现象称之为
折叠,结果就是合并的外边距称之为折叠外边距(collapsed margin)
BFC 解决外边距叠加举个例子吧,这里引用的是 W3C 文档中解释 BFC 的例子,如图所示:

上图只是一种假设的情况,实际是不存在的:
假设块
B1的margin-bottom为M1(B1没有子级也没有padding和border),浮动块F的高度为H,块B2的margin-top为M2(没有padding和border也没有子级),B2的clear为both。同样假设B2非空, 不考虑B2的clear属性,情况如图所示,B1和B2的margin合并了。B1的border-bottom边在y = 0处,F的top在y = M1处,B2的border-top边在y = max(M1,M2)处,F的bottom在y = M1 + H处
实际的情况是这样的:

源代码如下:
<!DOCTYPE html>
<html>
<head>
<title>BFC模型</title>
<style type="text/css">
html, body {
margin: 0;
}
.b1 {
margin-bottom: 50px;
height: 100px;
width: 200px;
background: #f3f3f3;
}
.f {
float: left;
height: 100px;
width: 100px;
background: #bfbfbf;
}
.b2 {
margin-top: 10px;
height: 100px;
width: 200px;
background: #f3f3f3;
}
</style>
</head>
<body>
<div class="b1">B1</div>
<div class="f">F</div>
<div class="b2">B2</div>
</body>
</html>
通过浏览器的开发者工具可以清晰的看到,B1 的 margin-bottom 和 B2 的 margin-top 合并了,于是我们想,怎么样解决合并?W3C 文档提供了解决这方面的方法,之一就是:
一个正常流元素的
margin-bottom与它下一个正常流的兄弟元素的margin-top会产生折叠,除非它们之间存在空隙(clearance)
这个 空隙 是什么呢?我们暂且后面再讨论,先为上面的代码 B2 的 CSS 属性添加一行
.b2 {
clear: left;
}
于是

我们再通过开发者工具查看,发现——外边距合并消失了!
其实这就是通过 clear 引用了一个 空隙,具体说下 空隙 :
先看 clear 属性:
- left
要求该盒的 border-top 边位于源文档中在此之前的元素形成的所有左浮动盒的 bottom 外边下方
- right
要求该盒的 border-top 边位于源文档中在此之前的元素形成的所有右浮动盒的 bottom 外边下方
- both
要求该盒的 border-top 边位于源文档中再次之前的元素形成的所有左浮动盒和右浮动盒的 bottom 外边下方
- none
对该盒相对于浮动(盒)的位置没有约束
值不为
none就隐含了要引入空隙(clearance)。空隙会阻止margin合并,并作为元素margin-top上方的空间。用来在竖直方向上推着元素越过浮动
计算一个设置了
clear的元素的空隙,先要确定该元素的border-top边的假定位置。该位置是当元素的clear属性为none时其border-top边实际所在的位置
如果该元素的
border-top边的假定位置没有越过相关的浮动(盒),那么就得引入空隙,并根据相关规则合并margin
然后,空隙的高度被设置为下面两者的较大值:
把块的
border-top边放在最低的将被clear的浮动(盒)的bottom外边,所必需的高度把块的
border-top边放在其假定位置(clear为none时的位置),所必需的高度
或者,空隙被精确地设置为把块的
border边放在最低的将被clear的浮动(盒)的border-bottom外边,所必需的高度
注意:空隙可以为负或者为
0
回到最上面的假设,计算一下这个 空隙 具体是多少呢?这里要计算两次 C1 和 C2 ,并取最大值 C :
- 第一次计算
既满足上面 条件1 的情况。
F(border-bottom) = B2(border-top) -> M1 + H = M1 + C1 + M2 -> C1 = H - M2
解释就是:B2 的顶部放在浮动块 F 的底部的位置等于 B1 的 border-bottom 加上 空隙 加上 B2 的 border-top
- 第二次计算
既满足上面 条件2 的情况。
max(M1, M2) = M1 + C2 + M2 -> C2 = max(M1, M2) - M1 -M2
解释就是: 把 B2 放在假定位置等于B1 的 border-bottom 加上 空隙 加上 B2 的 border-top
假设 max(M1, M2) < M1 + H , 再次推导:
C2 = max(M1, M2) - M1 - M2 < M1 + H - M1 - M2 = H - M2 -> C2 < H - M2
也即 C2 < C1 ,
所以 C = max(C1, C2) = C1
根据以上推导,得出以下结论:
-
如果
B1的margin-bottom和B2的margin-top的最大值小于B1的margin-bottom加上F的高度,那么 空隙C就是F的高度减去B2的margin-top -
如果
B1的margin-bottom和B2的margin-top的最大值大于B1的margin-bottom加上F的高度,那么 空隙C就是B1的margin-bottom或者B2的margin-top的值中最小的一个
说实话,我总结的自己都觉得绕啊(+﹏+)~
解决一些布局问题
如图,当我们没有对 DIV2 清楚浮动影响时的情况,这是因为 DIV1 为浮动元素并且重新建立了 BFC ,而 DIV2 仅处于常规流的 BFC 中。

这就需要我们为 DIV2 重新建立 BFC,例如添加 overflow: hidden , 其它方法也可(设置为非块盒的块容器,不推荐),之后如下图:

总结
其实 BFC 的应用很广,再次不在过多概述。作为一个前端开发人员,了解原理是非常重要的,我们以后遇到什么问题,都可以由此联想到这里。
送给大家一句话,“前端是坑,上手容易,精通不易” 。当然我不是讽刺,而是说前端这个行业要想做好,不能只停留在表面。要想成为大牛,还是要花些时间深入研究,花费的时间将是以后少走弯路的指南针!各位前端的朋友们,一起努力加油吧!