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