为了账号安全,请及时绑定邮箱和手机立即绑定

CSS学习笔记——深入理解BFC

2016.06.26 11:11 6449浏览

常常听人说起BFC的概念,做为一个只懂KFC的小白,今天整理了网上这方面知识,有兴趣的小伙伴可以学习下。

本文内容引用自诸多博客,故将作者姓名和文章地址放在本文最后



一.什么是BFC?

1.BFC的概念
BFC全称Block Formatting Context ,中文直译为块级格式上下文。它是 W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。
通俗的来说:BFC是一个独立的布局环境,我们可以理解为一个箱子(实际上是看不见摸不着的),箱子内部的元素无论如何翻江倒海,都不会影响到外部。转换为BFC的理解则是:BFC中的元素的布局是不受外界的影响(我们往往利用这个特性来消除浮动元素对其非浮动的兄弟元素和其子元素带来的影响。)并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。

2.BFC布局规则

   1.  在BFC下,内部的Box会在垂直方向,一个接一个地放置。
    2.  Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box    的margin会发生重叠
    3.  在BFC中,每一个盒子的左外边缘(margin-left)会触碰到容器的左边缘(border-left)(对于从右到左的格式来说,则触碰到右边缘),即使存在浮动也是如此。
    4.  BFC的区域不会与float box重叠。
    5.  计算BFC的高度时,浮动元素也参与计算

3.如何触发 BFC

触发 BFC 的条件如下:

  • 浮动元素,float 除 none 以外的值

  • 绝对定位元素,position(absolute,fixed)

  • display 为以下其中之一的值inline-block, table-cell, table-caption, flex,
    inline-flex

  • overflow 除了 visible 以外的值(hidden,auto,scroll)

二.BFC的应用


1.解决浮动塌陷问题

示例代码如下:

<style>
.parent {
    border: 2px solid #428bca;
}

.child {
    width: 100px;
    height: 100px;
    float: left;
    background: #3BD49E;
}
</style>
<div class="parent">
    <div class="child"></div>
</div>  

上述代码的效果是这样,可能和你想的有一些出入
图片描述
这其中的原理是这样:
• 如果父元素只包含浮动元素,且父元素未设置高度和宽度的时候,那么它的高度就会塌缩为零。
• 如果父元素不包含任何的可见背景,这个问题会很难被注意到,但是这是一个很重要的问题,上面的代码就是一个典型的例子。

为解决浮动塌陷的问题,也可以用 BFC 来解决(参见第四条约束规则)
为其父元素创建 BFC
修改父元素的 CSS 代码:

 .parent {
    border: 2px solid #428bca;
    overflow: auto;
  }

修改后就可以解决这个问题。如图所示
图片描述

2.自适应两栏布局
我们还可以运用BFC可以阻止元素被浮动元素覆盖的特性来实现自适应两栏布局。
为了示例演示,我们先定义两个div

<div class="aside"></div>  
<div class="main"></div>  

再定义CSS:

div {  
    width:300px;  
}   
.aside {  
      width: 100px;  
      height: 150px;  
      float: left;  
      background: black;  
}  
.main {  
     height:200px;  
     background-color:red;  
}

效果图如下
图片描述
这正满足了规范的第三条:
每个元素的左边,与包含的盒子的左边相接触,即使存在浮动也是如此。
所以如果我们需要将黑色区域撑到红色的左边,就需要利用规范的第四条:
BFC的区域不会与float重叠。
也就是说我们需要创造BFC区域。我们通过将红色区域的overflow设为hidden来触发BFC:

.main {  
      overflow:hidden;  
      height:200px;  
      background-color:red;  
    }  

效果如图所示,这时候左栏宽度固定,右栏宽度会随着浏览器宽度的调整而调整,从而实现一个自适应两栏布局。关于这方面内容张鑫旭前辈写过一篇博客建议大家看一下。
图片描述


3.合并外边距与BFC
在CSS当中,相邻的两个盒子(可能是兄弟关系也可能是祖先关系)的外边距可以结合成一个单独的外边距。这种合并外边距的方式被称为折叠,并且因而所结合成的外边距称为折叠外边距。

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

产生折叠的必备条件:margin必须是邻接的!
而根据w3c规范,两个margin是邻接的必须满足以下条件:

  • 必须是处于常规文档流(非float和绝对定位)的块级盒子,并且处于同一个BFC当中。

  • 没有线盒,没有空隙(clearance,下面会讲到),没有padding和border将他们分隔开

  • 都属于垂直方向上相邻的外边距,可以是下面任意一种情况:
    • 元素的margin-top与其第一个常规文档流的子元素的margin-top
    • 元素的margin-bottom与其下一个常规文档流的兄弟元素的margin-top
    • height为auto的元素的margin-bottom与其最后一个常规文档流的子元素的margin-bottom
    • 高度为0并且最小高度也为0,不包含常规文档流的子元素,并且自身没有建立新的BFC的元素的margin-top和margin-bottom

以上的条件意味着下列的规则:

  • 创建了新的BFC的元素(例如浮动元素或者'overflow'值为'visible'以外的元素)与它的子元素的外边距不会折叠

  • 浮动元素不与任何元素的外边距产生折叠(包括其父元素和子元素)

  • 绝对定位元素不与任何元素的外边距产生折叠
  • inline-block元素不与任何元素的外边距产生折叠

  • 一个常规文档流元素的margin-bottom与它下一个常规文档流的兄弟元素的margin-top会产生折叠,除非它们之间存在间隙(clearance)。

  • 一个常规文档流元素的margin-top 与其第一个常规文档流的子元素的margin-top产生折叠,条件为父元素不包含 padding 和 border ,子元素不包含 clearance。

  • 一个 'height' 为 'auto' 并且 'min-height' 为 '0'的常规文档流元素的 margin-bottom 会与其最后一个常规文档流子元素的 margin-bottom 折叠,条件为父元素不包含 padding 和 border ,子元素的 margin-bottom 不与包含 clearance 的 margin-top 折叠。

  • 一个不包含border-top、border-bottom、padding-top、padding-bottom的常规文档流元素,并且其 'height' 为 0 或 'auto', 'min-height' 为 '0',其里面也不包含行盒(line box),其自身的 margin-top 和 margin-bottom 会折叠。

(下面我们对不产生折叠的情况逐一分析。)

  1. 浮动和绝对定位不与任何元素产生 margin 折叠

原因:浮动元素和绝对定位元素不与其他盒子产生外边距折叠是因为元素会脱离当前的文档流,违反了上面所述的两个margin是邻接的条件同时,又因为浮动和绝对定位会使元素为它的内容创建新的BFC,因此该元素和子元素所处的BFC是不相同的,因此也不会产生margin的折叠。

demo:
css代码:

    <style>
            body{
                padding: 0;
                margin: 0;
                text-align: center;
            }
            .float-box{
                float:left;
                height: 100px;
                width: 100px;
                background: #9ACD32;
                margin-top: 10px;
            }
            .box{
                margin-top: 50px;
                height: 200px;
                width: 200px;
                background:lightskyblue;
                margin-bottom: 90px;
            }
            .absolute-box{
                position:absolute;
                margin-top: 100px;
                background: blueviolet;
                width: 200px;
                height: 200px;
            }
        </style>
    </head>
    <body>
    <div class="box">
    <div class="float-box">float-box</div>
    </div>
    <div class="absolute-box">absolute-box</div>
    </body>

设置浮动和绝对定位属性前后对比:
设置前:
图片描述
添加浮动和绝对定位属性后:
图片描述
但是浮动元素在脱离了当前的BFC后,并不会影响它后面的兄弟元素,后面的兄弟元素与浮动元素前面的元素依然在同一个BFC当中,所以,它们之间的margin还是会折叠的。下面我们对上面的demo做一下修改:

.brother-box{
                background: gold;
                margin-top: 20px;
            }
<body>
    <div class="box">
    <div class="float-box">float-box</div>
    <div class="brother-box">brother-box</div>
    </div>
    <div class="absolute-box">absolute-box</div>
</body>

我们可以看到brother-box元素顶到box元素的顶端,说明box元素和brother-box元素的上边距发生了重叠,这证实了前面的结论。

图片描述

下面我们来谈谈 'clearance' 这个神奇的东西,当浮动元素之后的元素设置clear以闭合相关方向的浮动时,根据w3c规范规定,闭合浮动的元素会在其margin-top以上产生一定的空隙,该空隙会阻止元素margin-top的折叠,并作为间距存在于元素的margin-top的上方。关于这个间距的计算稍微有点复杂,但实际工作中你并不需要去计算它,我们先来看看例子吧:
代码如下:

<style>
            body{
                padding: 0;
                margin: 0;
                text-align: center;
            }
            .box{
                height: 150px;
                width: 150px;
                background: darkgrey;
            }
            .float-box{
                float: left;
                height: 150px;
                width: 150px;
                background: #9ACD32;
            }
            .clear-box{
                margin-top: 150px;
                clear: both;
                height: 150px;
                width: 150px;
                background: lightskyblue;
            }
        </style>
    </head>
    <body>
    <div class="box">box</div>
    <div class="float-box">float-box</div>
    <div class="clear-box">clear-box</div>
    </body>

效果如图所示:
图片描述
上面的图中我们可以看到,我们为蓝色块设置的margin-top在低于或等于浮动元素的高度时,好像并没有起作用,只有蓝色块设置的margin-top大于浮动元素本身高度时才有效。
经过我的测试得知,使用清除浮动属性的元素,它的外边距塌陷规则变成如下规则:闭合浮动的盒子的border-top始终和浮动元素的margin-bottom底部重合。而在闭合浮动的盒子的margin-top上方,直到top盒子的margin-bottom底部这段距离,就是我们所说的clearance。

所以我们可以根据上面的结论得出一个公式:
闭合浮动元素的clearance = 浮动元素上下边距高度 + 浮动元素height +浮动元素的上下边框高度+浮动元素的上下内边距高度。

我用demo来演示上面的公式,这里分别为float元素添加10像素的padding和border。

CSS代码

.float-box{
                float: left;
                height: 150px;
                width: 150px;               
                padding: 10px;
                border: 10px solid pink;
            }
.clear-box{
                margin-top: 190px;
                clear: both;
                height: 150px;
                width: 150px;
                background: lightskyblue;
            }

效果如下,我们可以看到这里为了clear-box设置margin-top: 190px;时并没有它与float元素并没有产生边距,这证实我们前面的公式。
图片描述
通过上面验证,我们就可以知道有浮动元素时,闭合浮动元素的clearance是怎么计算的了。一个基本原则就是闭合浮动的元素的margin-top与浮动元素的margin-bottom重合。

PS:闭合浮动并不能使浮动元素回到原来的BFC当中,

注:inline-block元素与其兄弟元素、子元素和父元素的外边距都不会折叠(包括其父元素和子元素)

inline-block不符合w3c规范所说元素必须是块级盒子的条件,因为规范中又说明,块级盒子的display属性必须是以下三种之一:'block', 'list-item', 和 'table'。




写在最后

  • 我试图将BFC的概念以浅俗易懂的方式整理出来,这当中夹杂着我的一些个人理解,其中难免有不当之处,欢迎各位指出。

  • 如果你看完后本文后还没有理解BFC的概念呢,我建议你看一下我引用的这些文章。

本文引用内容地址及其作者:

参考资料:

w3c标准[英]
CSS魔法堂:说说Float那个被埋没的志向

点击查看更多内容
30人点赞

若觉得本文不错,就分享一下吧!

评论

相关文章推荐

正在加载中
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消