BFC的个人理解

1,475 阅读7分钟
一直记得我公司的技术Leader对我说的一句话: 
当我们学习一样知识的时候,我们可以通过“3W+H”(what,when,why,how)的方法来检验自己对该知识点的掌握程度,如果你对上述的“3W”都能十分清楚的回答上来,说明你对该知识点的掌握还可以,如果不能,则说明你根本就没掌握

下面就BFC用“3W”的方法,谈一下自己对它的个人见解:

What:什么是BFC

MDN官方说法:

块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。

视觉格式化模型(visual formatting model)

是用来处理文档并将它显示在视觉媒体上的机制(说白了,这个模型就是控制生成不同的盒子),视觉格式化模型定义了盒(Box)的生成,盒主要包括了块盒、行内盒、匿名盒(没有名字不能被选择器选中的盒)以及一些实验性的盒(未来可能添加到规范中)。盒的类型由display属性决定。

学过CSS的都知道,页面上的元素基本上都是一个个的盒子组成的(包括块盒、行内盒、匿名盒(没有名字不能被选择器选中的盒)),下面我们先来温习一下几个重要的盒子:

块盒(block box)

块盒有以下特性:

  • 当元素的CSS属性displayblocklist-itemtable时,它是块级元素 block-level;
  • 视觉上呈现为块,竖直排列;
  • 块级盒参与(块格式化上下文);
  • 每个块级元素至少生成一个块级盒,称为主要块级盒(principal block-level box)。一些元素,比如<li>,生成额外的盒来放置项目符号,不过多数元素只生成一个主要块级盒。

行内盒(inline box)

  • 当元素的CSS属性display的计算值为inlineinline-blockinline-table时,称它为行内级元素;
  • 视觉上它将内容与其它行内级元素排列为多行;典型的如段落内容,有文本(可以有多种格式譬如着重),或图片,都是行内级元素;
  • 行内级元素生成行内级盒(inline-level boxes),参与行内格式化上下文(inline formatting context)。同时参与生成行内格式化上下文的行内级盒称为行内盒(inline boxes)。所有display:inline的非替换元素生成的盒是行内盒;
  • 不参与生成行内格式化上下文的行内级盒称为原子行内级盒(atomic inline-level boxes)。这些盒由可替换行内元素,或 display 值为 inline-blockinline-table 的元素生成,不能拆分成多个盒;

匿名盒(anonymous box)

匿名盒也有份匿名块盒与匿名行内盒,因为匿名盒没有名字,不能利用选择器来选择它们,所以它们的所有属性都为inherit或初始默认值;

块盒和行内盒我们大家都比较熟悉了,下面举个简单的例子说明一下匿名盒:

 <div class="container"> 
       我是小妖怪,逍遥又自在 
       <h1>杀人不眨眼</h1>
       吃人不放盐  
</div>

什么样式也没加,其中.container就是一个默认的块元素,但是“我是小妖怪”也会生成一个盒子,由于我们无法用css选择器来选择它,所以这个盒子也就是匿名盒子。

总而言之:BFC是决定块盒子的布局以及浮动相互影响的区域,归根揭底,BFC就是在CSS渲染的时候形成的一个块级区域,关于这个区域的特点,请见下文。

HOW:怎样形成一个BFC

  • 根元素(html)或其它包含它的元素;
  • 浮动 (元素的float不为none);
  • 绝对定位元素 (元素的positionabsolutefixed);
  • 行内块inline-blocks(元素的 display: inline-block);
  • 表格单元格(元素的display: table-cell,HTML表格单元格默认属性);
  • overflow的值不为visible的元素;
  • 弹性盒 flex boxes (元素的display: flexinline-flex);

也就是说,满足上面的任何一个条件,就会形成一个BFC,其中最常见的就是overflow:hiddenfloat:left/rightposition:absolute。


BFC的范围

BFC包含创建该上下文元素的所有子元素,但不包括创建了新BFC的子元素的内部元素。

也就是说,一个BFC包含包含创建该上下文元素的所有子元素,但如果在一个BFC里面又生成了新的BFC,那么内部BFC的子元素将不受外部BFC的影响。也就是说对于一个具体的元素而言,其只能属于一个BFC,而不能同属于多个BFC。


BFC的特性:

  • 内部的盒会在垂直方向一个接一个排列(可以看作BFC中有一个的常规流);
  • 处于同一个BFC中的元素相互影响,可能会发生margin collapse(垂直方向margin重叠);
  • 每个元素的margin box的左边,与容器块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此;
  • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然(外部的元素也不会影响容器内部的子元素);
  • 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算(不会发生高度塌陷);
  • 浮动盒区域不叠加到BFC上(也就是说,浮动的元素不会遮挡住BFC);

  • 下面就以上的几个特性用例子实际说明一下:

    1. 内部的盒会在垂直方向一个接一个排列(可以看作BFC中有一个的常规流);

     <style> 
          .bfc{
                position: absolute; //使用了绝对定位,形成一个BFC
                left: 100px;
                top:100px;
                width: 200px;
                height: 200px;
                border: 1px solid #ffcccc;
                background:rgba(220, 14, 129, 0.5)
            }        
        .item1,.item2{
            border: 1px solid green; 
            padding: 10px;
            margin: 20px
          }    
    </style>

    <div class="container bfc"  >
            <div class="item1">2</div>
            <div class="item2">3</div>
    </div>

    其结果就是这样子:


    可见在BFC内部形成正常的文档流,但却发生了方向上的margin重叠,要解决这个重叠,可以在内部重新生成一个新的BFC:

    <div class="container bfc"  >
            <div class="item1">2</div>
            <div class="bfc-inner">
                <div class="item2">3</div> 
           </div>
     </div>

    <style>
        .bfc{ 
            position: absolute;
            left: 100px;
            top:100px;
            width: 200px;
            height: 200px;
            border: 1px solid #ffcccc; 
            background:rgba(220, 14, 129, 0.5)
        }
        .item1,.item2{
            border: 1px solid green; 
            padding: 10px;
            margin: 20px;
         }        
        .bfc-inner{
            float: left ;// 新的bfc 
         }        
        .bfc-inner .item2{ 
           width: 100% 
         }
    </style>


    margin的重叠不见了,因为item2已经不属于外部的BFC了

    2. 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算(不会发生高度塌陷);

     <style>
        .bfc{
            border: 1px solid #000;
         }        
        .item1,.item2{
                border: 1px solid green;
                width: 100px; 
                height: 100px;
                float: left
         }        
        .item1{            
            background:blueviolet;
            opacity: 0.4;
         }        
        .item2{
            background:brown;
            opacity: 0.4;
        }    
    </style>

     <div class="bfc"  >
        <div class="item1">2</div>
        <div class="item2">3</div>
     </div>

    会发生典型的高度塌陷:


    但是当我们给.bfc元素添加样式position:absolute或者float:left的时候,就会形成一个BFC,高度塌陷就消失了。



    3.  浮动盒区域不叠加到BFC上(也就是说,浮动的元素不会遮挡住BFC);

    还是使用上面的元素,不过样式变成了这个样子:

    <style>        
        .bfc{
             border: 1px solid #000;
           }        
        .item1,.item2{
               border: 1px solid green; 
        }        
        .item1{
                background:blueviolet;
                width: 100px;
                height: 100px;
                float: left
         }        
        .item2{
             width: 300px;
             height: 100px;
             background:brown;
         }
    </style>

    这样的话,.item2就会被.item1遮挡


    但是当我们给.item2添加一个样式:overflow:hidden的时候(形成了一个BFC),就不会被遮挡了。


    WHEN和WHY

    对于BFC,在我看来更像是一个策略,可以使用BFC的特性来解决不同的布局问题,比如高度塌陷,比如浮动遮挡的问题,垂直方向margin折叠等,其实我们会发现我们无意间就会使用BFC,理解并灵活使用BFC能让我们在布局的时候避免很多坑。

    参考文章:

    juejin.cn/post/684490…(网易考拉:学习BFC)