这次搞懂BFC

1,078 阅读10分钟

什么是BFC

BFC全称是Block Formatting Context,即块格式化上下文。它是CSS2.1规范定义的,关于CSS渲染定位的一个概念。

在介绍清楚BFC之前,我们先简单回顾一下CSS中的盒模型:盒模型包括内容(Content)、Padding、边框(Border)和Margin 。

而块级格式化上下文(BFC)是页面中一个相对独立的模块,它定义了它内部的块级盒子(块级元素的盒子模型)如何排布和布局。html的根元素<html></html>标签就会产生一个BFC区域。

BFC规则

1、内部的块级盒子会在垂直方向,一个接一个地放置。

2、块级盒子垂直方向的距离由margin决定。属于同一个BFC的两个相邻块级盒子的margin会发生重叠。

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

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

5、BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

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

创建新的BFC

根元素或其它包含它的元素;

浮动(元素的float不为none);

绝对定位元素(元素的position为absolute或fixed);

行内块inline-blocks(元素的display: inline-block);

表格单元格(元素的display: table-cell,HTML表格单元格默认属性);

overflow的值不为visible的元素;

弹性盒 flex boxes(元素的display: flex或inline-flex);

BFC的区域

<div id="div0" class="main BFC">
    <div id="div1" class="div1 margin-100px">元素1</div>
    <div id="div2" class="div2 margin-100px">元素2</div>
    <div id="div3"class="div3 margin-100px BFC">
            <div id="div5" class="child">子元素1</div>
            <div id="div6" class="child">子元素2</div>
    </div>
</div>

BFC区域包含创建BFC元素的所有子元素,但不包括又创建BFC元素下面的所有子元素。

看上面这段代码,id=div0的元素创建了一个BFC区域,该元素下面的id为div1div2div3都属于div0所创建的BFC区域下。

又因为id=div3的div又创建了一个BFC区域,所以div5div6就不再属于div0所创建的BFC区域内,而属于div3创建的BFC区域内。

知道了BFC的区域的划分,我们通过每一个实例,来对BFC的规则做一个演示和体会

BFC代码实例

1、内部的块级盒子会在垂直方向,一个接一个地放置。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        
    </script>
    <style>
        .main{
            margin: 10px;
            border: 3px solid red;
        }
        .div1{
            opacity: 0.5;
            background: greenyellow;
            width: 100px;
            height: 100px;
      
        }
        .div2{
            opacity: 0.5;
            background: rebeccapurple;
            width: 100px;
            height: 100px;
        }
        .BFC{
            display: inline-block;
        }
    </style>
</head>
<body>
    <div id="div0" class="main BFC">
            <div id="div1" class="div1">元素1</div>
            <div id="div2" class="div2">元素2</div>
    </div>
</body>
</html>

结果:

我们看到我们给div0设置了一个BFC的css样式:display:inline-block,此时我们给div0创建了一个BFC。而它内部的div元素都带有默认的css属性:display:block,因此它们会生成块级盒子,在BFC区域内垂直排布。这就是我们大家都知道的常规流。

其实我们会发现我们当初学习HTML和CSS时,很多块级元素的规则其实就是BFC的规则。

2、块级盒子垂直方向的距离由margin决定。属于同一个BFC的两个相邻块级盒子的margin会发生重叠。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        
    </script>
    <style>
        .main{
            margin: 10px;
            border: 3px solid red;
        }
        .div1{
            opacity: 0.5;
            background: greenyellow;
            width: 100px;
            height: 100px;
        }
        .div2{
            opacity: 0.5;
            background: rebeccapurple;
            width: 100px;
            height: 100px;
        }
        .BFC{
            display: inline-block;
        }
        .margin-100px{
            margin: 100px;
        }
    </style>
</head>
<body>
    <div id="div0" class="main BFC">
            <div id="div1" class="div1 margin-100px">元素1</div>
            <div id="div2" class="div2 margin-100px">元素2</div>
    </div>
</body>
</html>

结果:

这就是BFC中经常发生的边距重叠的情况。而且重叠的情况会根据重叠的块级盒子的margin值有关,在这里即:div1的margin-bottom和div2的margin-top值有关系。

分三种情况:

当div1的margin-bottom和div2的margin-top值都为正数,则重叠时,取最大的值展示。

当div1的margin-bottom和div2的margin-top值一正一负时,则重叠时,取他们之间的差值展示。如下图展示:

当div的margin-bottom和div2的margin-top值都为负数,则重叠时,取他们绝对值最大的margin值。如下图展示:

如何解决margin边距重叠

根据BFC规则:属于同一个BFC的两个相邻块级盒子的margin会发生重叠。因此我们只要使div1与div2不属于同一个BFC即可:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        
    </script>
    <style>
        .main{
            margin: 10px;
            border: 3px solid red;
        }
        .div1{
            opacity: 0.5;
            background: greenyellow;
            width: 100px;
            height: 100px;
        }
        .div2{
            opacity: 0.5;
            background: rebeccapurple;
            width: 100px;
            height: 100px;
        }
        .BFC{
            display: inline-block;
        }
        .margin-100px{
            margin: 100px;
        }
    
        
    </style>
</head>
<body>
    <div id="div0" class="main BFC">
            <div id="div1" class="div1 margin-100px">元素1</div>
            <div class="BFC">
                    <div id="div2" class="div2 margin-100px">元素2</div>
            </div>
    </div>
</body>
</html>

此时我们将div2包裹在一个新的div下面,通过给这个新的div设置display:inline-block新创建了一个BFC,因此此时的div1和div2不在一个BFC区域中,所以他们的margin值不会发生重叠。

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

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        
    </script>
    <style>
        .main{
            margin: 10px;
            border: 3px solid red;
        }
        .div1{
            opacity: 0.5;
            background: greenyellow;
            width: 100px;
            height: 100px;
        }
        .div2{
            opacity: 0.5;
            background: rebeccapurple;
            width: 100px;
            height: 100px;
        }
        .BFC{
            display: inline-block;
        }
        .margin-100px{
            margin: 100px;
        }
        .float-left{
            float: left;
        }
    
        
    </style>
</head>
<body>
    <div id="div0" class="main BFC">
            <div id="div1" class="div1 margin-100px float-left">元素1</div>
            <div id="div2" class="div2 margin-100px">元素2</div>
    </div>
</body>
</html>

这里我们仔细想想为什么会发生这样的情况:

1、首先我们的div1和div2处于同一个BFC区域中,我们这里暂且把这个BFC称作BFC0; 2、我们给div1设置了float:left; 3、由于div1进行了漂浮,而div2与div1处于BFC0中,所以div2和div1还是会发生重叠;

要解决漂浮重叠,我们也可以通过BFC进行解决,我们依旧给div2放到一个新的BFC区域中,那么根据BFC的规则,BFC的区域不会与float box重叠,就可以解决。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        
    </script>
    <style>
        .main{
            margin: 10px;
            border: 3px solid red;
        }
        .div1{
            opacity: 0.5;
            background: greenyellow;
            width: 100px;
            height: 100px;
        }
        .div2{
            opacity: 0.5;
            background: rebeccapurple;
            width: 100px;
            height: 100px;
        }
        .BFC{
            overflow: auto;
        }
        .margin-100px{
            margin: 100px;
        }
        .float-left{
            float: left;
        }
    </style>
</head>
<body>
    <div id="div0" class="main BFC">
            <div id="div1" class="div1 margin-100px float-left">元素1</div>
            <div class="BFC">
                    <div id="div2" class="div2 margin-100px">元素2</div>
            </div>
    </div>
</body>
</html>

结果:

此时我们通过给div2新建了一个BFC区域,因此它没有与div1漂浮发生重叠。

这时候仔细的小伙伴可能会问,根据BFC的规则1,为什么div2没有垂直方向独占一行?

这是因为div1发生了漂浮,它会影响它后面的一个元素,因此并不是div2没有在垂直方向独占一行,而是因为div1漂浮的缘故,div2和div1一起占了一行,而又因为新建了一个BFC,因此div2没有和div1重叠在一起,所以此时看起来他们并没有垂直排布。其实是div2独占了一行,而由于div1由于漂浮脱离了文档流,导致的。

5、BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之也如此。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        
    </script>
    <style>
        .main{
            margin: 10px;
            border: 3px solid red;
        }
        .div1{
            opacity: 0.5;
            background: greenyellow;
            width: 100px;
            height: 100px;
        }
        .div2{
            opacity: 0.5;
            background: rebeccapurple;
            width: 100px;
            height: 100px;
        }

        .child{
            width: 50px;
            height: 50px;
            background: orange;
            margin: 10px;
        }
        .BFC{
            display: inline-block;
        }
        .margin-100px{
            margin: 100px;
        }
        .float-left{
            float: left;
        }
    </style>
</head>
<body>
    <div id="div0" class="main BFC">
            <div id="div1" class="div1 float-left">元素1</div>
            <div id="div2" class="div2">
                    元素2
                    <div class="child"></div>
                    <div class="child"></div>
                    <div class="child"></div>
            </div>

    </div>
</body>
</html>

结果:

我们可以看到由于div1进行漂浮,div2中的文字和设置了child样式的div被受到了div1漂浮的影响。

我们给div2创建新的BFC区域,即可解决问题。

此时,div2不再受到div1的影响。

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

当我们使用漂浮的时候,我们经常会遇到高度塌陷的问题。例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        
    </script>
    <style>
        .main{
            margin: 10px;
            border: 3px solid red;
        }
        .div1{
            opacity: 0.5;
            background: greenyellow;
            width: 100px;
            height: 100px;
        }
        .BFC{
            display: inline-block;
        }
        .float-left{
            float: left;
        }
        
    </style>
</head>
<body>
    <div id="div0" class="main">
        <div class="div1 float-left">元素1</div>
    </div>
</body>
</html>

结果:

我们可以看到此时我们没有给div0设置BFC,同时使div1进行漂浮,我们看到div0计算高度的时候没有包含div1的高度。这就是我们所说的高度塌陷的情况。

我们也可以用BFC进行解决这个问题,我们给div0创建一个BFC,此时它在计算高度时,会同时包括div1的高度。

复盘:

BFC其实就是CSS范畴的知识,它对块级元素生成的块级盒子进行了一些规则的限制,使这些块级盒子按照规则在html文档中排布和布局。或许你之前并没有系统的学习过BFC这个知识点,但你一定是通过经验之谈,对BFC多多少少是有了解的。

相关资料:

MDN块格式化上下文

MDN视觉格式化模型

学习 BFC (Block Formatting Context)

我是大麦,如果喜欢我的文章,请给我一颗小心心。