BFC的应用

回顾一下BFC

BFC是Block Formatting Context的缩写,译为块级格式上下文。如果你对块级框,块框,行级框,行框等概念不了解可以先去看看框模型与视觉格式化模型

  • BFC其实就是块级框的布局规则,相当于CSS页面布局的地基,并非什么神奇的概念,大家习以为常的一些东西其实最终的解释是可以归于BFC的。
  • 创建了BFC的元素规定了内部的块级框如何布局,并且使该框在页面上形成一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然
  • 在BFC中,框会从包含块的顶部开始,一个接一个地,垂直向下地摆放
    我们常说block元素很霸道,会扩展到与父元素同宽,block元素会垂直排列,其实是因为BFC的约束。
  • 两个兄弟框之间的垂直距离由margin属性来决定。在同一个BFC中,相邻的块级框之间的垂直外边距会出现折叠。
    常见的外边距折叠,其实并不是bug,而是由于BFC的约束。
  • 在BFC中,每个框的左外边距边要紧贴其包含块的左边(对于从左往右的格式化),否则相反。即使在有浮动的情景下也是如此,除非框创建了一个新的BFC(在这种情况下该框可能会为了避开浮动框而变窄)。
  • BFC的区域不会与float box重叠
  • 计算BFC的高度时,浮动元素也参与计算
    大家肯定知道overflow:hidden可以清浮动,哈哈,就是因为这个啦
  • 下列情况将创建一个BFC:
    • 根元素
    • 浮动 (元素的 float 不为 none)
    • 绝对定位元素 (元素的 position 为 absolute 或 fixed)
    • 行内块 inline-blocks (元素的 display: inline-block)
      注意:虽然display:inline-block的元素本身被当做一个原子行级框来格式化,但其内部会被当作块框来格式化,所以它是可以创建BFC的,而它本身是参与IFC的。
    • 表格单元格 (元素的 display: table-cell,HTML表格单元格默认属性)
      注意:虽然display: table-cell的元素本身不能生成块级框,但是可以包含块框,所以可以创建BFC。
    • 表格标题 (元素的 display: table-caption, HTML表格标题默认属性)
    • overflow 的值不为 visible的元素
    • 弹性盒子 flex boxes (元素的 display: flex 或 inline-flex)
正式来用BFC做些事

1.防止margin折叠

1
2
3
4
<body>
<p>我是第一个萌萌哒的p,我有30px的下边距</p>
<p>我是第二个萌萌哒的p,我有50px的上边距</p>
</body>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style type="text/css">
p{
width: 200px;
height: 100px;
text-align: center;
}
p:first-child{
margin-bottom: 30px;
background: #bda;
}
p:last-child{
margin-top: 50px;
background: #fbc;
}
</style>

结果是这样的:
外边距折叠咯,只有50px,而不是80px,这是根据BFC的下列规则而成

两个兄弟框之间的垂直距离由margin属性来决定。在同一个BFC中,相邻的块级框之间的垂直外边距会出现折叠。

注意其中的关键词,在同一个BFC中,那么我们只需让他们不在同一个BFC中就可以防止折叠了。这可以有多种方案,比如说给第一个p或第2个p加float、给第一个p或第2个p外面包裹一层容器,并触发该容器生成一个BFC、按理说第一个p或第2个p加overflow:hidden应该也可以可是实际测试并不可以,我也不懂这是why。这是demo,大家可以修改注释来查看不同的方案

2.清浮动
我们来看一个非常常见的例子

1
2
3
4
5
6
<body>
<ul>
<li>我是浮动的子元素</li>
<li>我是浮动的子元素</li>
</ul>
</body>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
ul {
border: 5px solid #fcc;
width: 300px;
}
li {
border: 5px solid #bda;
width:100px;
height: 100px;
float: left;
list-style: none;
}
</style>

结果是这样的:
父元素的高度塌陷了,根据BFC的下列规则

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

为了达到清浮动的效果,我们可以触发ul创建BFC,那么ul在计算高度时,ul内部的浮动元素li也会参与计算,这也可以有几个方案,比如ul也float、ul加overflow:hidden,为ul设置position:absolute。当然实际应用中要考虑这些属性的一些其他的影响,这是demo,大家可以修改注释来查看不同的方案。

3.自适应布局的一种方式
现在我们想创建一个两列布局,其中左侧宽度不定,右侧宽度要自适应占用剩下的空间。
我们写下如下代码

1
2
3
4
<body>
<div class="side">side</div>
<div class="main">main</div>
</body>

1
2
3
4
5
6
7
8
9
10
11
12
13
body{
margin: 0;
}
.side {
width: 300px;
height: 150px;
float: left;
background: #fcc;
}
.main {
height: 200px;
background: #bda;
}

结果如图所示

可以看到是很习以为常的一个现象,main部分的左外边距紧贴包含块的左边,虽然有side部分的这个浮动元素存在。这是因为的如下规则:

在BFC中,每个框的左外边距边要紧贴其包含块的左边(对于从左往右的格式化),否则相反。即使在有浮动的情景下也是如此,除非框创建了一个新的BFC(在这种情况下该框可能会为了避开浮动框而变窄)。
再有如下规则:
BFC的区域不会与float box重叠

那么利用这个规则,我们就只需要为main创建一个新的BFC,那就可以解决与float box重叠的问题了,最简单的加一个overflow:hidden。如果此时你想用浮动来生成BFC,那么你需要考虑一下浮动本身带来的影响,比如说:它的宽度变为刚好包裹住内容。除非你设置了一个非auto的宽度。同样如果采用绝对定位等方式来生成,也要考虑这些属性本身的一些效果。而不能认为只要能创建BFC就可以拿来实现这个效果。我们采用overflow:hidden,这个相对来说影响小一些。给main加上后就成为下面这样啦

side部分的宽度可以是不定宽的,main部分将根据side部分的宽度自适应的占据剩余宽度。但是该方法不能让main部分先渲染,因为我们在HTML中将它写在了side部分的后面。当然两栏自适应布局还有其他方式实现。
demo演示