我终于理解了 BFC

281 阅读6分钟

前言

在正题开始之前,想说两点:

  1. 对 BFC 的转变:知道 => 理解

    在此之前的我,肯定知道 BFC 的存在,那么说说对 BFC 的理解?很难,无从下手

    此时的我,也许真能对 BFC 说道说道了。

  2. 支持王红元老师(coderwhy)

    很多知识都是跟着王老师学习的,总能让我醍醐灌顶,YYDS。

正题

这是一篇观后总结笔记,看了B站 《coderwhy 官方账号》的对 BFC 知识点的视频讲解,所以下面的很多图片是来源于截图,见笑了。

FC

FC:formatting context,译为格式上下文,可以简单的理解为一种环境

官方解释:

BFC1.png

  1. 元素(块级元素,行内元素)只要在标准流中,就会属于一个 FC
  2. 块级元素属于 BFC,行内元素属于 IFC
  3. 但是不会存在一个元素既属于 BFC,又属于 IFC

总结:FC 是一种环境,在该环境下就遵循该环境下的规则。

BFC

BFC: block formatting context,译为块级格式化上下文

BFC 环境形成的条件

知道了 BFC 的存在,那么怎么才能看见它呢?

MDN:块格式化上下文

  • 根元素(<html>
  • 浮动元素(float 值不为 none
  • 绝对定位元素(position 值为 absolutefixed
  • 行内块元素(display 值为 inline-block
  • 表格单元格(display 值为 table-cell,HTML 表格单元格默认值)
  • 表格标题(display 值为 table-caption,HTML 表格标题默认值)
  • 匿名表格单元格元素(display 值为 tabletable-rowtable-row-grouptable-header-grouptable-footer-group(分别是 HTML table、tr、tbody、thead、tfoot 的默认值)或 inline-table
  • overflow 值不为 visibleclip 的块元素
  • display 值为 flow-root 的元素
  • contain 值为 layoutcontentpaint 的元素
  • 弹性元素(display 值为 flexinline-flex 元素的直接子元素),如果它们本身既不是 flexgrid 也不是 table 容器
  • 网格元素(display 值为 gridinline-grid 元素的直接子元素),如果它们本身既不是 flexgrid 也不是 table 容器
  • 多列容器(column-countcolumn-width (en-US) 值不为 auto,包括column-count1
  • column-span 值为 all 的元素始终会创建一个新的 BFC,即使该元素没有包裹在一个多列容器中 (规范变更, Chrome bug)

只要满足了上面的条件,那么就会形成 BFC 环境(环境就是一种可知不可见的东西)。

BFC 环境下的规则

在理解规则之前,先来思考几个问题:

  1. 为什么块级盒子会一行一行的布局?
  2. 为什么盒子的布局是从浏览器的左边开始布局,而不是右边?
  3. 盒子之间的间距是通过 margin 来进行设置的,为什么?

我相信存在很多码友跟我一样的想法,这还用说,这不是约定俗成的嘛。是的,人是可以通过约定来形成规则,但是针对机器却是不行,它必须内部存在一种规则,根据该规则来执行对应的任务。

那么就来了,机器里面的规则(也可以说浏览器里面的规则)是什么?没有错,里面布局存在的规则就是 BFC规则IFC规则

官方解释:

BFC2.png

  1. 规则一:在BFC中,盒子的布局是在垂直方向上一个一个的排列,从容器的顶部开始。
  2. 规则二:盒子与盒子之间的距离,通过 margin 属性设置。
  3. 规则三:在同一个BFC环境中,两个垂直之间的盒子,都设置了 margin 属性,则会产生折叠(collapse)。
  4. 规则四:每个盒子的布局都是从容器的左边缘开始布局的。

这就是 BFC 环境中的规则,浏览器里面的规则就是基于 BFC 环境下的规则的(当然还有 IFC 环境下的规则)。

到了这里,相信大家或多或少对 BFC 有认知了吧。

BFC 代码演示

bfc.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>bfc</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    .box1 {
      background-color: red;
      width: 300px;
      height: 100px;
    }
    .box2 {
      background-color: blue;
      width: 200px;
      margin-top: 30px;
      height: 100px;
    }
  </style>
</head>
<body>
  <div class="box1"></div>
  <div class="box2"></div>
</body>
</html>

效果图:

BFC3.png

相信大家都知道效果如何,也相信大家都能解释了为什么是这种效果。

提示:这是 html 根标签已经形成了 BFC,那么就会遵守 BFC 规则。

BFC 的应用

  1. 解决 margin 折叠的问题。
  2. 解决清除浮动,高度塌陷的问题。

margin 折叠问题

BFC 环境的有个规则:就是在同一个 BFC 环境下,相邻的两个盒子,存在margin 会进行折叠,取最大值。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>bfc</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    .box1 {
      background-color: red;
      width: 300px;
      height: 100px;
      margin-bottom: 100px;  /* margin-bottom */
    }
    .box2 {
      background-color: blue;
      width: 200px;
      margin-top: 30px; /* margin-top */
      height: 100px;
    }
  </style>
</head>
<body>
  <div class="box1"></div>
  <div class="box2"></div>
</body>
</html>

无论是 box1 盒子 还是 box2 盒子都在 html 根标签形成 BFC 中,所以会产生折叠。

BFC4.png 那么想要不发生折叠,就让其中的一个盒子处于另外一个 BFC 环境中

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>bfc</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .container {
        overflow: auto; /* 形成 BFC 环境 */
      }
      .box1 {
        background-color: red;
        width: 300px;
        height: 100px;
        margin-bottom: 100px;
      }
      .box2 {
        background-color: blue;
        width: 200px;
        margin-top: 30px;
        height: 100px;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="box1"></div>
    </div>
    <div class="box2"></div>
  </body>
</html>

BFC5.png 这样就不会产生折叠,因为没有处于同一个 BFC 环境中。

清除浮动的问题

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>bfc</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .container {
        /* overflow: auto; */
      }
      .item {
        background-color: red;
        height: 50px;
        width: 100px;
        float: left; /* 浮动 */
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="item"></div>
      <div class="item"></div>
      <div class="item"></div>
      <div class="item"></div>
    </div>
  </body>
</html>

在浮动布局的时候,就会产生一种现象,盒子内部设置了浮动,盒子的高度就塌陷了。那么解决方案就两种:

  1. 清除浮动
  2. 形成 BFC 环境

主要说下第二种解决方案。

先来关注一下,为什么设置了浮动,高度就塌陷了?是因为浮动元素,就已经脱离标准流了,不会把自身的高度汇报给父元素,从而父元素就没有高度了。

那么形成了 BFC 环境之后,就能把脱标元素的高度汇报给父元素了,这种说法对不对?答案是不对的。

因为脱标元素不仅仅只有浮动,还有绝对定位,那么把子元素设置为 position: absolute,并且再把父元素形成 BFC环境 之后,观察现象,也是没有汇报高度给父元素的,所以上面的那种说法是不成立的。

那么正确的结论是怎么样的呢?

官方解释:

BFC6.png 这张截图挺模糊的,但是大致的意思就是:

针对 height:auto 的 BFC 环境的盒子,高度的计算如下:

  1. 如果子元素是行内元素,那么高度就是行高的顶部到行高的底部距离
  2. 如果子元素是块级元素,那么高度就是最上面盒子的上边缘到最下面盒子的下边缘的距离
  3. 如果是绝对定位元素,那么将直接被忽略
  4. 如果是浮动元素,那么就会增加高度,完全包裹子元素的下边缘

这也就解释了上面的那种说法是错误的。因为 BFC 盒子,发现子元素是浮动元素,就增加自身的高度,来完全包裹子元素,并不是子元素汇报高度给父元素。

上面满足的条件:

  1. 形成 BFC。
  2. height 为 auto。

总结

虽然是看了 coderwhy 视频来写的笔记,但是还是避免不了错误,所以说有误的话,请说出来,学习学习。

总的来说,对 BFC 的认识,我已经上升了一个台阶,你们呢?