什么是 BFC_

本文由 简悦 SimpRead 转码, 原文地址 juejin.cn

对于 BFC 的概念以及应用场景一直都不是很明白,今天着重去了解了一下,做了以下总结。

前言

对于 BFC 的概念以及应用场景一直都不是很明白,今天着重去了解了一下,做了以下总结。

BFC 的定义

在解释 BFC 是什么之前,需要先介绍 Box、Formatting Context 的概念。

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 中只有 BFC 和 IFC, CSS3 中还增加了 GFC 和 FFC。

BFC

  BFC(Block Formatting Context)直译为 “块级格式化范围”。是 W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用 当涉及到可视化布局的时候,Block Formatting Context 提供了一个环境,HTML 元素在这个环境中按照一定规则进行布局。一个环境中的元素不会影响到其它环境中的布局。比如浮动元素会形成 BFC,浮动元素内部子元素的主要受该浮动元素影响,两个浮动元素之间是互不影响的。这里有点类似一个 BFC 就是一个独立的行政单位的意思。 也可以说 BFC 就是一个作用范围。可以把它理解成是一个独立的容器,并且这个容器的里 box 的布局,与这个容器外的毫不相干

怎样才能形成 BFC

  1. float 的值不能为 none
  2. overflow 的值不能为 visible
  3. display 的值为 table-cell, table-caption, inline-block 中的任何一个
  4. position 的值不为 relative 和 static

BFC 的约束规则

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

看到以上的几条约束,让我想起学习 css 时的几条规则

Block 元素会扩展到与父元素同宽,所以 block 元素会垂直排列 垂直方向上的两个相邻 DIV 的 margin 会重叠,而水平方向不会 (此规则并不完全正确) 浮动元素会尽量接近往左上方(或右上方) 为父元素设置 overflow:hidden 或浮动父元素,则会包含浮动元素

BFC 的作用

1. 不和浮动元素重叠

如果一个浮动元素后面跟着一个非浮动的元素,那么就会产生一个覆盖的现象,很多自适应的两栏布局就是这么做的。比如下图的效果,参考例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<style>
    body {
        width: 300px;
        position: relative;
    }

    .aside {
        width: 100px;
        height: 150px;
        float: left;
        background: #f66;
    }

    .main {
        height: 200px;
        background: #fcc;
    }
</style>
<body>
    <div></div>
    <div></div>
</body>
复制代码

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/4/160bfae6ea247e92~tplv-t2oaga2asx-watermark.image

案例分析: 很明显,.aside 和. mian 重叠了。试分析一下,由于两个 box 都处在同一个 BFC 中,都是以 BFC 边界为起点,如果两个 box 本身都具备 BFC 的话,会按顺序一个一个排列布局,现在. main 并不具备 BFC,根据 BFC 布局规则第 3 条

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

虽然存在浮动的元素 aslide,但 main 的左边依然会与包含块的左边相接触 根据 BFC 布局规则第四条:

BFC 的区域不会与 float box 重叠

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

1
2
3
4
.main {
    overflow: hidden;
}
复制代码

当触发 main 生成 BFC 后,这个新的 BFC 不会与浮动的 aside 重叠。因此会根据包含块的宽度,和 aside 的宽度,自动变窄。效果如下:

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/4/160bfae6ebe2f403~tplv-t2oaga2asx-watermark.image

2. 清除元素内部浮动

案例代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<style>
    .par {
        border: 5px solid #fcc;
        width: 300px;
    }

    .child {
        border: 5px solid #f66;
        width:100px;
        height: 100px;
        float: left;
    }
</style>
<body>
    <div>
        <div></div>
        <div></div>
    </div>
</body>
复制代码

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/4/160bfae723e59a6f~tplv-t2oaga2asx-watermark.image

根据 BFC 布局规则第六条:

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

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

1
2
3
4
.par{
  overflow:hidden
}
复制代码

加入上面样式,即可解决问题,效果如下:

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/4/160bfae6ea1917ce~tplv-t2oaga2asx-watermark.image

3. 防止垂直 margin 重叠
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<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>
复制代码

页面如下:

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/4/160bfae6e9f5730f~tplv-t2oaga2asx-watermark.image

两个 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
20
<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>
        <p>Hehe</p>
    </div>
</body>
复制代码

效果如下:

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/1/4/160bfae6ebd2b70d~tplv-t2oaga2asx-watermark.image

按照 BFC 的定义,只有同属于一个 BFC 时,两个元素才有可能发生垂直 Margin 的重叠,这个包括相邻元素,嵌套元素,只要他们之间没有阻挡 (例如边框,非空内容,padding 等) 就会发生 margin 重叠。 因此要解决 margin 重叠问题,只要让它们不在同一个 BFC 就行了,但是对于两个相邻元素来说,意义不大,没有必要给它们加个外壳,但是对于嵌套元素来说就很有必要了,只要把父元素设为 BFC 就可以了。这样子元素的 margin 就不会和父元素的 margin 发生重叠了。

部分内容来自 BFC 背后神奇的原理