BFC的深入理解

1,931 阅读6分钟

常见定位方案

在讲 BFC 之前,我们先来了解一下常见的定位方案,定位方案是控制元素的布局,有三种常见方案:

  • 普通流 (normal flow)

    在普通流中,元素按照其在 HTML 中的先后位置至上而下布局,在这个过程中,行内元素水平排列,直到当行被占满然后换行,块级元素则会被渲染为完整的一个新行,除非另外指定,否则所有元素默认都是普通流定位,也可以说,普通流中元素的位置由该元素在 HTML 文档中的位置决定。

  • 浮动 (float)

    在浮动布局中,元素首先按照普通流的位置出现,然后根据浮动的方向尽可能的向左边或右边偏移,其效果与印刷排版中的文本环绕相似。

  • 绝对定位 (absolute positioning)

    在绝对定位布局中,元素会整体脱离普通流,因此绝对定位元素不会对其兄弟元素造成影响,而元素具体的位置由绝对定位的坐标决定。

BFC的定义

Formatting context(格式化上下文) 是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。

那么 BFC 是什么呢?

BFC 即 Block Formatting Contexts (块级格式化上下文),它属于上述定位方案的普通流。

具有 BFC 特性的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器所没有的一些特性。

通俗一点来讲,可以把 BFC 理解为一个封闭的大箱子,箱子内部的元素无论如何翻江倒海,都不会影响到外部。

触发 BFC

只要元素满足下面任一条件即可触发 BFC 特性:

  • body 根元素
  • 浮动元素:float 除 none 以外的值
  • 绝对定位元素:position (absolute、fixed)
  • display 为 inline-block、table-cell、table-caption、flex等
  • overflow 除了 visible 以外的值 (hidden、auto、scroll)

BFC的作用

  • 清除内部浮动

我们在布局时经常会遇到这个问题:对子元素设置浮动后,父元素会发生高度塌陷,也就是父元素的高度变为0。解决这个问题,只需要把父元素变成一个BFC就行了。常用的办法是给父元素设置overflow:hidden。

<style>
    .box1{
        width:100px;
        height:100px;
        float:left;
        border: 1px solid #000;
    }
    .box2{
        width:100px;
        height:100px;
        float:left;
        border: 1px solid #000;
    }
    .box{
        background:yellow
    }
</style>
<body>
    <div class="box">
        <div class="box1"></div>
        <div class="box2"></div>
    </div> 
</body>

由于容器内两个div元素浮动,脱离了文档流,父容器内容宽度为零(即发生高度塌陷),未能将子元素包裹住。解决这个问题,只需要把把父元素变成一个BFC就行了。常用的办法是给父元素设置overflow:hidden。

  • 垂直margin合并

在CSS当中,相邻的两个盒子的外边距可以结合成一个单独的外边距。这种合并外边距的方式被称为折叠,并且因而所结合成的外边距称为折叠外边距。

折叠的结果:

  1. 两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值。
  2. 两个相邻的外边距都是负数时,折叠结果是两者绝对值的较大值。
  3. 两个外边距一正一负时,折叠结果是两者的相加的和。

这个同样可以利用BFC解决。 如果想要避免外边距的重叠,可以将其放在不同的 BFC 容器中。

  1. 相邻兄弟元素margin重叠问题
<style>
    p{
        color: #fff;
        background: #888;
        width: 200px;
        line-height: 100px;
        text-align:center;
        margin: 100px;
    }
</style>
<body>
    <p>ABC</p>
    <p>abc</p>
</body>

上面例中两个P元素之间距离本该为200px,然而实际上只有100px,发生了margin重叠。遇到这种情形,我们如何处理? 只需要在p外面包裹一层容器,并触发该容器生成一个BFC。那么两个P便不属于同一个BFC,就不会发生margin重叠了。

<style>
    p{
        color: #fff;
        background: #888;
        width: 200px;
        line-height: 100px;
        text-align:center;
        margin: 100px;
    }
    .wrap{
      overflow:hidden;
    }
</style>
<body>
  <p>ABC</p>
  <div class="wrap">
    <p>abc</p>
  </div>
</body>

  1. 父子元素margin重叠问题
<style>
    .box{
    width:100px;
    height:100px;
    background:#ccc;
    }
    .wrap {
      background:yellow;
    }
    .wrap h1{
      background:pink;
      margin:40px;
    }
</style>
<body>
    <div class="box">box</div>
    <div class="wrap">
      <h1>h1</h1>
    </div>
</body>

上图wrap元素与h1元素之间l理论上本该有个40px的上下margin值,然而实际上父子元素并没有存在margin值,与此同时,两个div元素的间距为40px。遇到这种情形,我们如何处理? 处理方法其实有很多,**在wrap元素中添加:overflow:hidden;或者overflow:auto;使其父元素形成一个BFC;也可以在wrap元素中添加border:1px solid;或是padding:1px;**这些都可以有效解决父子元素margin重叠问题。

  • 创建自适应两栏布局

这个方法可以用来实现两列自适应布局,也就是左边的宽度固定,右边的宽度自适应。如果我们改变文字的大小或者左边浮动元素的大小,两栏布局的结构依然没有改变!

<style>
    *{
    margin: 0;
    padding: 0;
    }
    .box {
        width:300px;
        border: 1px solid #000;
    }
    .img {
        float: left;
    }
    .info {
        background: #f1f1f1;
        color: #222;
    }
</style>
<body>
    <div class="box">
        <img src="03.jpg" alt="" class="img">
        <p class="info">信息信息信息信息信息信息</p>
    </div>
</body>

一般情况下,它是这样的

但是当文字多了以后...

显然,这是文字受到了图片浮动的影响。当然,如果你想做文本绕排的效果,浮动是不二之选。不过在这里,这显然不是我们想要的。此时我们可以为P元素的内容建立一个BFC,让其内容消除对外界浮动元素的影响。给文字加上overflow:hidden

两栏布局就完成了。我们改变图片的大小:

两栏布局的结构依然没有改变,如此就实现了两栏自适应布局。

参考链接