BFC是啥?(清除浮动与外边距合并问题)

BFC是什么?

入门FE了那么久,BFC这个名词倒是第一次知道,特地前去了解了一下,幡然醒悟,这不就是平常我们写block和inline的标准文档流吗?(恕我不专业的描述方式),估计是自己知识面太窄了,不然咋会在学习CSS的时候错过捏,呵呵哒。

Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with ‘overflow’ other than ‘visible’ (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.
————W3C

中文直译为块级格式上下文(或块级上下文)。其实就是我们平常的块级元素自顶向下排列,同级之间的containing block顶部一个接一个垂直排列,水平方向上撑满宽度。
因为两个相邻的BFC之间距离由margin决定,在同一个BFC内部,两个垂直方向相邻的块级元素margin值会"共用",导致塌陷。也是经典的外边距塌陷问题。

以下方法可以创建一个新的块级执行上下文(BFC):
  • 浮动元素
  • 绝对定位元素
  • 块级元素以及块级容器(比如inline-block、table-cell、table-capation)
  • overflow值不为visible的块级盒子
    当然,root元素会自动生成一个BFC,这个应该很好理解,毕竟需要一个根BFC来布局
执行规则:
  1. 在一个块级排版上下文中,盒子是从包含块顶部开始,垂直的一个接一个的排列的。每个盒子的左外边是触碰到包含块的左边的(对于从右向左的排版,则相反)
  2. 相邻两个盒子之间的垂直的间距是被margin属性所决定的,在一个块级排版上下文中相邻的两个块级盒之间的垂直margin是折叠的。

注意:这里着重了解一下,什么是同一个BFC、根BFC,有何区别


我们来实现它。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            div {
                width: 200px;
                height: 100px;
            }
            .a {
                background-color: red;
            }
            .b {
                background-color: orange;
            }
            .c {
                background-color: deepskyblue;
            }
            .d {
                background-color: black;
                margin: 20px;
            }
            .e {
                background-color: green;
                margin: 20px;
            }
        </style>
    </head>
    <body>
        <div class="a"></div>
        <div class="b"></div>
        <div class="c"></div>
        <div class="d"></div>
        <div class="e"></div>
    </body>
</html>
示例1
实例一

这是最普通的块级排版上下文,现在问题来了,这里有新的BFC吗?对比那4个方法......没有对吗,所以,这里的所有class都是同一种BFC或者说同一个BFC(这个准确些吧),他们遵守执行规则第一项,依次向下排列......
.d.e我加了margin值,但是我们发现,两者中间的垂直margin并没有生效,否则应该是40px才对。
这就是执行规则2生效了。

  1. 垂直margin合并
    在CSS当中,相邻的两个盒子的外边距可以结合成一个单独的外边距。这种合并外边距的方式被称为折叠,并且因而所结合成的外边距称为折叠外边距。
    折叠的结果:
  • 两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值。
  • 两个相邻的外边距都是负数时,折叠结果是两者绝对值的较大值。
  • 两个外边距一正一负时,折叠结果是两者的相加的和。
  • 这个同样可以利用BFC解决。关于原理在前文已经讲过了。
    ————引用自简书苏星河

所以我们用BFC解决margin合并,如何解决呢?就是建立一个新的BFC,使得他俩没有关系。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            * {
                margin: 0;
                padding: 0;
            }
            div {
                width: 200px;
                height: 100px;
            }
            .d {
                background-color: black;
                margin: 20px;
                display: inline-block;
            }
            .e {
                background-color: green;
                margin: 20px;
            }
        </style>
    </head>
    <body>
        <div class="d"></div>
        <div class="e"></div>
    </body>
</html>
示例二
示例二

在其中一个div的display改为inline-block,创建一个新的BFC,使得两者BFC所属不同。


浮动父容器塌陷问题
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            .wrap {
                border: solid 1px red;
                width: 250px;
            }
            .a,.b {
                width: 100px;
                height: 50px;
                background-color: orange;
                float: left;
            }
            .b {
                background-color: blue;
            }
        </style>
    </head>
    <body>
        <div class="wrap">
            <div class="a"></div>
            <div class="b"></div>
        </div>
    </body>
</html>
示例三

我是这么理解的,内部的两个div由于float,所以不在属于root BFC,则外容器内部不再有同样的BFC,判定为无内容(这里其实有歧义),所以红色框没有撑开。

从BFC的角度看解决方法


我们调整一下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            .wrap {
                border: solid 5px red;
                width: 250px;
                overflow: hidden;
            }
            .a,.b {
                width: 100px;
                height: 50px;
                background-color: orange;
                float: left;
            }
            .b {
                background-color: blue;
            }
        </style>
    </head>
    <body>
        <div class="wrap">
            <div class="a"></div>
            <div class="b"></div>
        </div>
    </body>
</html>
示例三
通过对父容器添加overflow:hidden,改变它的BFC,这样处于同样的BFC下的容器就会将子div内容算入,撑开父容器。

BFC与文字环绕
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            .ct {
                border: solid 1px black;
            }
            .aside {
                width: 50px;
                height: 50px;
                background-color: red;
                float: left;
            }
            .content {
                background-color: orange;
            }
        </style>
    </head>
    <body>
        <div class="ct">
            <div class="aside"></div>
            <div class="content">公园里还有一处烧烤区,一边看着远处耸立的高楼,一边吃着烧烤,也许脸书的员工就是喜欢这种自然田园的风光。
说实话,在硅谷这个寸土寸金的地方,脸书总部里能有这么大一块地作为公园我觉得是挺奢侈的,但是真的要比较起来,国内很多公园都比这个要做地精致和好看。连作者本人也这么觉得:</div>
        </div>
    </body>
</html>
文字盒子被挤压,文字少
文字增多

如何做到和右图一样呢?
先看看造成这个现象的原因

橙色区域为line-boxs,从表面看,文字盒子被浮动红盒子挤压到了左边,实际上,由于红盒子浮动,是新的BFC,文字盒子实际上是从父容器左边开始的,文字盒子实际上处于浮动元素的下方。文字盒子进行了移位,水平收缩为浮动元素提供了空间。
随着文字增加,文字盒子最终将会环绕在浮动元素下方,也就是现在这样。

我们如果能移动文字盒子,也就是文字的容器,就可以使它不环绕了。此处想一想W3C的概念:

In a block formatting context, each box’s left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box’s line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itselfmay become narrowerdue to the floats).
在BFC中,每个盒子的左外边框紧挨着左边框的包含块(从右到左的格式化时,则为右边框紧挨)。即使在浮动里也是这样的(尽管一个盒子的边框会因为浮动而萎缩),除非这个盒子的内部创建了一个新的BFC(这种情况下,由于浮动,盒子本身将会变得更窄),

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            .ct {
                border: solid 1px black;
            }
            .aside {
                width: 50px;
                height: 50px;
                background-color: red;
                float: left;
            }
            .content {
                background-color: orange;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <div class="ct">
            <div class="aside"></div>
            <div class="content">公园里还有一处烧烤区,一边看着远处耸立的高楼,一边吃着烧烤,也许脸书的员工就是喜欢这种自然田园的风光。
说实话,在硅谷这个寸土寸金的地方,脸书总部里能有这么大一块地作为公园我觉得是挺奢侈的,但是真的要比较起来,国内很多公园都比这个要做地精致和好看。连作者本人也这么觉得:</div>
        </div>
    </body>
</html>

文字盒子添加overflow:hidden,使得处于新的BFC中,就不会根据上面的W3C概念规则来绘制了。


MDN——外边距合并
苏星河:【CSS】深入理解BFC原理及应用
CSS深入理解流体特性和BFC特性下多栏自适应布局
CSS布局基础:BFC
理解CSS中BFC
BFC神器背后的原理


推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 12,154评论 1 91
  • relative:生成相对定位的元素,通过top,bottom,left,right的位置相对于其正常位置进行定位...
    zx9426阅读 667评论 0 2
  • 1.浮动元素有什么特征?对父容器、其他浮动元素、普通元素、文字分别有什么影响? 浮动元素不在文档的普通流中,它可以...
    饥人谷_Young丶K阅读 180评论 0 0
  • 一,浮动元素有什么特征?对父容器、其他浮动元素、普通元素、文字分别有什么影响? 浮动模型是一种可视化格式模型,浮动...
    DeeJay_Y阅读 572评论 0 4
  • 1.在什么场景下会出现外边距合并?如何合并?如何不让相邻元素外边距合并?给个父子外边距合并的范例 概念:在CSS当...
    饥人谷_任磊阅读 354评论 0 3