Fork me on GitHub
余鸢

对BFC的理解

BFC是什么?

在解释 BFC 是什么之前,会涉及到CSS布局的一些基本概念,需要先介绍 Box、Formatting Context的概念。

  • 对css有了解的朋友肯定都知道盒式模型这个概念,对一个元素设置css,首先需要知道这个元素是block还是inline类型。而BFC就是用来格式化块级盒子,同样管理inline类型的盒子还有IFC,以及其他的FC。那首先我们就来看看FC的概念。
  • Formatting Context:指页面中一个渲染区域,并且拥有一套渲染规则,他决定了其子元素如何定位,以及与其他元素的相互关系和作用。
  • BFC:块级格式化上下文,它是指一个独立的块级渲染区域,只有Block-level Box参与,该区域拥有一套渲染规则来约束块级盒子的布局,且与区域外部无关。

  Box: CSS布局的基本单位

  Box 是 CSS 布局的对象和基本单位, 直观点来说,就是一个页面是由很多个 Box 组成的。元素的类型和 display 属性,决定了这个 Box 的类型。 不同类型的 Box, 会参与不同的 Formatting Context(一个决定如何渲染文档的容器),因此Box内的元素会以不同的方式渲染。让我们看看有哪些盒子:

  • block-level box:display 属性为 block, list-item, table 的元素,会生成 block-level box。并且参与 block fomatting context;
  • inline-level box:display 属性为 inline, inline-block, inline-table 的元素,会生成 inline-level box。并且参与 inline formatting context;
  • run-in box: css3 中才有, 这儿先不讲了。

  Formatting context

  Formatting context 是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。最常见的 Formatting context 有 Block fomatting context (简称BFC)和 Inline formatting context (简称IFC)。

  CSS2.1 中只有 BFCIFC, CSS3 中还增加了 GFCFFC。

  BFC 定义

  BFC(Block formatting context)直译为”块级格式化上下文”。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。

​ 块级元素是那种源文档被格式化为可视块了的元素,然后使这个元素变成块级元素的display属性取值如下: ‘block’, ‘list-item’, 和 ‘table’。

​ 块级盒block-level box是这种参与了块级排版上下文的一种盒子,每个块级元素都生成了一个包含后代盒子和生成的内容的主要块级盒,并且这个盒子参与了任何定位的计算。

图1

​ 块级元素会自动生成一个块级盒block-level box,这是块级盒block-level-box的盒模型构成,它表明的是块级盒自身的结构构成。

​ margin、border、padding、content分别定义了元素四种边,然后每种类型的边的四条边定义了一个盒子,分别是content box、padding box、border box、margin box,而决定块盒在包含块中与相邻块盒的垂直间距的便是margin-box,这个margin-box是始终存在的,即使它的margin为0,试看下面的代码:

1
<div style="width: 400px;height: 400px; background:#ff4400;"></div>

图2

此时我没有为这个div设置margin值,但是这个div还是有一个默认为0的margin的,试看下面的浏览器生成的computed style:

图3

可以看到div默认的margin为0

哪些元素会生成BFC?

  • 根元素
  • float属性不为none
  • position为absolute或fixed
  • display为inline-block, table-cell, table-caption, flex, inline-flex
  • overflow不为visible

  BFC布局规则:

  1. 内部的Box会在垂直方向,一个接一个地放置。
  2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
  3. 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
  4. BFC的区域不会与float box重叠。
  5. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
  6. 计算BFC的高度时,浮动元素也参与计算

BFC在布局中的应用

那么BFC有何用?

  1. 清除浮动

  2. 防止 margin 重叠

  3. 多栏布局的一种方式
清除浮动
  • 使得父元素包含子元素,常见的方式是为父元素设置overflow:hidden或者浮动父元素。根本原因在于创建BFC的元素,子浮动元素也会参与其高度计算,即不会产生高度塌陷问题。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<style>
.par {
border: 5px solid #fcc;
width: 300px;
}
.child {
border: 5px solid #f66;
width:100px;
height: 100px;
float: left;
}
</style>
<body>
<div class="par">
<div class="child"></div>
<div class="child"></div>
</div>
</body>

页面:

图7

根据BFC布局规则第六条:

计算BFC的高度时,浮动元素也参与计算

为达到清除内部浮动,我们可以触发par生成BFC,那么par在计算高度时,par内部的浮动元素child也会参与计算。

代码:

1
2
3
.par {
overflow: hidden;
}

效果如下:

图8

防止margin重叠:
  • 同一个BFC中俩个相邻的box才会发生重叠与方向无关,不过由于上文提到的第一条限制,我们甚少看到水平方向的margin重叠。这在IE这中是个特例,IE可以设置write-mode。
  • 要阻止margin重叠,只要将俩个元素别放在一个BFC中即可(可以用上文提到的方式让相邻元素其中一个生成BFC)。阻止俩个相邻元素的重叠看起来没有意义,主要用于嵌套元素。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
p {
color: #f55;
background: #fcc;
width: 200px;
line-height: 100px;
text-align:center;
margin: 100px;
}
</style>
<body>
<p>Haha</p>
<p>Hehe</p>
</body>

页面:

图5

两个p之间的距离为100px,发送了margin重叠。
根据BFC布局规则第二条:

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

我们可以在p外面包裹一层容器,并触发该容器生成一个BFC。那么两个P便不属于同一个BFC,就不会发生margin重叠了。
代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<style>
.wrap {
overflow: hidden;
}
p {
color: #f55;
background: #fcc;
width: 200px;
line-height: 100px;
text-align:center;
margin: 100px;
}
</style>
<body>
<p>Haha</p>
<div class="wrap">
<p>Hehe</p>
</div>
</body>

效果如下:

图6

多栏布局的一种方式
  • 上文提到的一条规则:与浮动元素相邻的已生成BFC的元素不能与浮动元素互相覆盖。利用该特性可以作为多栏布局的一种实现方式。
  • 这种布局的特点在于左右俩栏的宽度固定,中间栏可以根据浏览器宽度自适应。 图4

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
body {
width: 600px;
position: relative;
}
.left {
width: 200px;
height: 350px;
float: left;
background: red;
}
.right {
height: 400px;
background: blue;
}

效果:

图9

根据BFC布局规则第3条:

每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。

根据BFC布局规则第四条:

BFC的区域不会与float box重叠。

我们可以通过通过触发right生成BFC, 来实现自适应两栏布局。

加上:

1
2
3
4
5
.right {
height: 400px;
background: blue;
overflow:hidden;
}

效果如下:

图10

这样实现了左栏的宽度固定,右栏可以根据浏览器宽度自适应,依次类比,三栏布局也是很好实现的

参考链接:

http://www.cnblogs.com/lhb25/p/inside-block-formatting-ontext.html

http://web.jobbole.com/84808/