文章目录
  1. 1. 盒模型
    1. 1.1. Box 类型
    2. 1.2. Formatting Context
  2. 2. BFC 定义
  3. 3. BFC 布局规则
    1. 3.1. 内部Box垂直放置
    2. 3.2. 同BFC垂直margin重叠
      1. 3.2.1. demo.rule1+2
    3. 3.3. BFC起始包含孩子们
      1. 3.3.1. demo.rule3
    4. 3.4. BFC不和float重叠
      1. 3.4.1. demo.rule4
    5. 3.5. BFC独立容器
      1. 3.5.1. demo.rule5
    6. 3.6. BFC的高度会计算浮动子
      1. 3.6.1. demo.rule6
  4. 4. 能解决什么问题?
  5. 5. 如何触发BFC?
  6. 6. 参考

目录:
一、BFC
二、IE 中的 hasLayout
三、BFC 和 hasLayout 的联系和区别

盒模型

Box(盒模型)是 CSS 布局的对象和基本单位,一个页面就是由多个 Box 组成的。Box 的类型要么是 Block,要么是 Inline,但不能同时属于这两者。在渲染页面的时候,每种 Box 都有它自己的 Formatting Context(格式化上下文):Block Box 在 Block Formatting Context 里格式化,Inline Box 在 Inline Formatting Context 里格式化。

也就是说,任何被渲染的元素都属于一个 Box,不是 Block 就是 Inline。即使是未被任何元素包裹的文本,根据不同的情况,也会属于匿名的 Block Box 或者 Inline Box。

简而言之,我们可以这么理解:所有的元素都被划分到了对应的 Formatting Context 里。 BFC 就是这里的 Block Formatting Context(块级格式化上下文)。

Box 类型

不同类型的 Box 会参与不同的 Formatting Context。

  • Block-level Box 块级盒模型
    display:block|list-item|table,会生成 Block-level Box,并参与 Block Formatting Context
  • Inline-level Box
    display: inline|inline-block|inline-table,会生成 Inline-level Box,并参与 Inline Formatting Context
  • run-in box (CSS3才有)

注意:
display:block 会【参与】Block Formatting Context。此处的表述是“参与”,而并不是它自己就是 BFC。前者是一个 CSS 属性设置,后者是一个布局的上下文环境,两者是不同维度的东西。虽然,有时候一个 div 恰好就是一个独立的 BFC,但那也是因为另一个东西【让它成为了】一个不受外界干扰的世外桃源式的上下文环境。

Formatting Context

Formatting Context(格式化上下文)是一个决定如何渲染文档的容器。它是 W3C CSS2.1 规范中的概念。它代表页面中的一块渲染区域,并有一套自己的渲染规则。决定了其子元素如何定位,也决定了它和其他元素的关系和相互作用。

最常见的 Formatting Context 有两种:Block Formatting Context(BFC)和 Inline Formatting Context(IFC)。
CSS2.1 中只有 BFC 和 IFC。CSS3 中增加了 GFC 和 FFC。

BFC 定义

Block Formatting Context 直译:块级格式化上下文。它是一个独立的渲染区域,规定了内部的 Block-level Box 如何布局,并且与区域外部毫不相干。

BFC 布局规则

下面依次介绍 BFC 的6个布局规则。

内部Box垂直放置

规则1: 内部的 Box 会在垂直方向上一个接一个地放置

同BFC垂直margin重叠

规则2:Box 垂直方向的距离由 margin 决定,属于同一个 BFC 的两个相邻 Box 的垂直 margin 会发生重叠。

demo.rule1+2

因为:在普通流中,两个相邻的块框在垂直方向上的外边距会发生合并,即处于同一个BFC的两个块的垂直margin会重叠。
所以,在BFC布局中,就会出现一些看起来“诡异”的情况。来看下面的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<style>
.parent1, .parent2 {
background-color: #f3eeee;
margin: 10px 0;
}
.child1, .child2 {
width: 100px;
height: 100px;
margin: 10px auto;
}
.child1 {
background-color: #f9bcc6;
}
.child2 {
background-color: #97e79c;
}
</style>
<div class="parent1">
<div class="child1"></div>
<div class="child2"></div>
</div>
<div class="parent2">
<div class="child1"></div>
<div class="child2"></div>
</div>

垂直外边距的表现:
1.两子之间的垂直外间距,是重叠的。两父之间也重叠。
2.当不给父设置外间距时,内部子伸出来的10px也能把两父撑开。即子的外间距,伸到了外边。
3.当给父也设置外间距时,父和子的垂直外边距,是重叠的。
原因:
因为这6个box是同属于一个BFC的(最外层的根元素),所以它们在垂直方向上不分你我的一个一个放,且外边距的逻辑也很单纯地就一个挨一个+垂直合并。

以上布局之所以如此,关键就在于:它们是在同一个BFC里的。
那么,当它们不在同一个BFC里时,情况会如何呢?我们给 .parent1 设置 overflow:hidden; 让 .parent1 变成一个独立的BFC。如下。

1
2
3
4
5
<style>
.parent1 {
overflow: hidden;
}
</style>

此处,我们重点关注与上面例子的不同之处:
1.父1内,父和子的垂直外间距的关系变了:
(1)父和子的垂直外间距不重叠了:因为它们目前不属于同一个BFC
(2)子的外边距,也不会伸出来了
原因:
父1被触发成独立的BFC后,它就是一个独立的渲染区域,它内部的布局是完全独立的-不受外界的任何影响。
父和子目前就不在同一个BFC里了,子所在的独立渲染区域是父div.parent1,而父所在的独立渲染区域是根元素html。
所以,它两是完全独立的,谁不影响谁,各渲染各。于是,垂直外间距也就不重叠了。

相同之处是:
1.父1内:两个子之间的垂直外间距,还和以前一样
2.父1和父2之间,本质上没发生什么变化。还是当初那两个平等的box
原因:这两个子依然是处于同一个BFC—只不过不是根元素了而是其父div。而两个父,也是依然处于同一个BFC—且还是之前的根元素,没变。

BFC起始包含孩子们

规则3:每个元素 margin box 的左边,与包含块 content box 的左边相接触。即时浮动也是如此。
若是从右至左渲染的,则是每个元素 margin box 的右边和包含块 content box 的右边相接触。

说明:
此处的包含块,是它的父BFC(上下文环境),而不是它的直接父元素(Dom结构层面)。
再次强调,BFC是个上下文布局环境,而父元素是结构上的。两者不是一个东东。

demo.rule3

div.parent1 有 10px 的内边距和外边距,div.child 有 10px 的外边距。以下是代码和UI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<style>
.parent1 {
background-color: #fafafa;
overflow: hidden;
margin: 10px;
padding: 10px;
border: 1px solid #ddd;
}
.child {
height: 100px;
margin: 10px;
}
.child1 {
background-color: #f9bcc6;
width: 100px;
}
.child2 {
background-color: #97e79c;
}
.child3 {
background-color: #d7d7f3;
width: 100px;
}
</style>
<div class="parent1">
<div class="child child1"></div>
<div class="child child2"></div>
<div class="child child3"></div>
</div>


三个 child 都是被包含在包含块的内容区域里的,即每个 .child 元素 margin box 的最外侧和包含块 content box 左侧对齐。

下面,我们把 .child1 改成左浮动,.child3 改成右浮动。如下:

1
2
3
4
5
6
7
8
<style>
.child1 {
float: left;
}
.child3 {
float: right;
}
</style>

说明:

  1. 左浮动的元素 .child1 和包含块 content box 的左边接触。
  2. 右浮动的元素 .child3 和包含块 content box 的右边接触。
  3. 正常的div元素,宽度是“满”的。其左侧临接包含块 content box 的左边,右侧临接包含块 content box 的右边。

与上例相比,除了由于左右浮动而导致的位置变动外,.child3 和 .child2 之间的间距也变了:之前是10px(它两的垂直外边距重叠),现在是20px(外间距没有重叠)。
原因:
.child3 设置了浮动之后,就触发了自己的 BFC — 致使它自己成了一个独立的渲染区域。故和 .child2 再也不会因为都是普通的Box而发生 margin 重叠了。

BFC不和float重叠

规则4:BFC 的区域不会和 float box 重叠

demo.rule4

继续上面的例子,我们给 .child2 设置 overflow:hidden 触发其 BFC。

1
2
3
4
5
<style>
.child2 {
overflow: hidden;
}
</style>

此时,.child1和.child2的内容区域分开了,是因为.child2 此时也是独立的渲染区域,它不再和左浮动的.child1重叠了。至此,这三个child分别都是独立的BFC,彼此领土互不侵犯。

BFC独立容器

规则5:BFC 就是页面上一个隔离的独立容器。容器里的子元素不会影响到外面的元素

独立…独立到什么程度?— 不影响外面的元素…莫非是不论里面发生了什么惊天地泣鬼神的事情,外面的容器依然淡定的守在那里?
来,我们拿demo试试。

demo.rule5

首先,我们给.child1里放一对和容器等大小的猪猪,100*100px的。

1
2
3
4
5
6
<div class="parent1">
<div class="child child1">
<img src="http://p3.qhimg.com/t01210f8d5202b3fdc7.jpg" width="100" height="100">
</div>
<div class="child child2"></div>
</div>

好,现在猪猪长大了,变成了150*150px,来看看它的样子:

1
<img src="http://p3.qhimg.com/t01210f8d5202b3fdc7.jpg" width="150" height="150">

瞧,位置上丝毫没把 .child2 给“挤”挪挪,而是就这么赤裸裸地越界了。出了.child1 依然傲娇。为什么?因为我爹是BFC啊,独立有权。
再看 .child1 和 .parent 之间的关系,同样,高度上也潇洒地“溢”了出来。

—课间八卦2秒中—
至此,我们也可以看到,虽然BFC很独立自强自力更生,但它却在一定程度上破坏了网页本身的布局—让内容不自适应了。下面在“如何触发”小节里再详聊,此处先打个预防针。人无完人,布局也一样。

BFC的高度会计算浮动子

规则6:在计算 BFC 的高度时,浮动的子元素也会参与计算。

背景:
在 CSS2.1 中,高度的计算规则是:在普通流中,计算块级非替换元素的高度时,浮动子元素不参与计算。
所以,会经常导致一些看起来好像不符合预期的怪现象:子元素(子是float的)明明有高度,但父却 height:0。导致下方的内容冲上来了…我们来看下面的例子。

demo.rule6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<style>
.parent1 {
background-color: #fafafa;
border: 1px solid red;
}
.child1 {
float: left;
height: 100px;
width: 100px;
background-color: #f9bcc6;
}
</style>
<div class="parent1">
<div class="child1"></div>
</div>
<div>
我是下方文字
</div>

现在,给 .parent 设置 overflow:hidden; 以触发其BFC。这时,父容器就被“撑”开了。

能解决什么问题?

上节结合实例,说明了 BFC 的布局规则,那么它能解决的问题也就比较明显了。此处简要概况下:

  1. 不和浮动的元素重叠 【规则4】 //最常见的是两栏自适应布局:左float触发BFC,右也触发BFC
  2. 清除内部浮动 【规则6】 //也比较常用
  3. 防止垂直 margin 合并 【规则2】 //主动用的比较少。无非是更能理解和解释 “为什么它两的垂直margin不合并了?!!”

以上这些所谓的能解决的问题,其实都是BFC本身的特性使然:因为BFC内部的元素和外部的元素不相互影响。

之前吧,自己对BFC的认识有个误区:最耳熟能详的是BFC能解决的那些问题,对它也仅仅停留在了概念的熟悉上而已。使用CSS两年,现在再重新梳理一遍后,就觉得:与其了解它能解决什么问题,倒不如直接问自己两个问题:“什么是BFC?”、“它有什么特性—是怎么布局的?”。能解决的问题仅仅是个表象,而“它之所以是它”才是此知识点的关键。

如何触发BFC?

  1. 根元素 html
  2. float:left|right //不是none的浮动元素
  3. position: absolute|fixed //绝对定位+fix定位,反正不同于正常流
  4. overflow不是visible的,overflow:hidden|scroll|auto
  5. display:inline-block|table-cell|table-caption|flex|inline-flex

说到可以触发 BFC 布局的属性们,此处又想提一点:每种触发属性都有其适用情况,要有理由地选择。因为不同属性都有自带技能,eg.
2.float:left|right; 会让元素脱离正常文档流,而失去元素本身自带的流体特性
3.position: absolute|fixed,同样,也会脱离正常文档流….而且还脱离的很彻底呢(同float相比的话)
4.overflow:hidden; 超出的内容会被截断…但它却能很好的保持元素本身的流体特性
5.display:inline-block; 虽然宽度能很好的自适应(相对float来讲-不用设置固定width),也能形成独立的渲染上下文。但它毕竟是宽度再也不“满格”了

这点,张鑫旭的博客 《CSS深入理解流体特性和BFC特性下多栏自适应布局》 中的 4.BFC元素家族与自适应布局面面观 里写的比较详细。感兴趣的同学,可以去拜读下。

综合考虑这几种方式自带的“副作用”,我们一般会选择最“无害”的方式。实际情况中使用最多的是这两个:
overflow:hidden;
display:inline-block; //ie6-7不支持
zoom: 1; //ie专属,ie特有的 hasLayout,下篇博客再聊这点

参考

BFC神奇背后的原理:http://melonh.com/css/2014/01/10/the-magic-bfc.html
BFC和IE的hasLayout:http://www.cnblogs.com/pigtail/archive/2013/01/23/2871627.html
CSS深入理解流体特性和BFC特性下多栏自适应布局:http://www.zhangxinxu.com/wordpress/tag/bfc/

文章目录
  1. 1. 盒模型
    1. 1.1. Box 类型
    2. 1.2. Formatting Context
  2. 2. BFC 定义
  3. 3. BFC 布局规则
    1. 3.1. 内部Box垂直放置
    2. 3.2. 同BFC垂直margin重叠
      1. 3.2.1. demo.rule1+2
    3. 3.3. BFC起始包含孩子们
      1. 3.3.1. demo.rule3
    4. 3.4. BFC不和float重叠
      1. 3.4.1. demo.rule4
    5. 3.5. BFC独立容器
      1. 3.5.1. demo.rule5
    6. 3.6. BFC的高度会计算浮动子
      1. 3.6.1. demo.rule6
  4. 4. 能解决什么问题?
  5. 5. 如何触发BFC?
  6. 6. 参考