神奇的BFC以及被忽略的东西

BFC是CSS中一个非常重要的概念,经常用来清除浮动以及处理外边距折叠,但BFC到底是个什么东西却很难准确的表达清楚,国内的相关技术文档基本都不全面,本文的目的就是对BFC的方方面面做个整理,当然未必会一次性整理到位,后面如果发现有遗留错误的会继续补充。
 
什么是BFC?
BFC(Block formatting context)译为"块格式化上下文",是一个独立的渲染区域,规定了内部的子元素如何布局,并且与这个区域外部的关系。
 
什么情况下会产生BFC?
a、根元素(注意:这里必须手动设置,默认不触发BFC)
b、float属性不为none
c、position为absolute或fixed
d、display为inline-block, table-cell, table-caption, flex, inline-flex
e、overflow不为visible(除非该值已经被传播到视口)
 
BFC布局规则是怎样的?
a、内部的Box会在垂直方向,一个接一个地放置。
b、Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
c、每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
d、BFC的区域不会与float box重叠。
e、BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
d、计算BFC的高度时,浮动元素也参与计算
相关解读:
a、并不是只有块元素才会生成BFC,任何元素只要符合条件即可生成BFC
 
BFC应用及原理解析
 
1、自适应两栏布局
.aside {
width: 100px;
height: 150px;
float: left;
background: #f66;
}
.main {
height: 200px;
background: #fcc;
}
<div class="aside"></div>
<div class="main"></div>

效果图:

 

关于效果图的解释,有资料用BFC布局规则c来解释:
 
  “每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。”
 
其实这样解释不完全正确,第三条布局生效的前提是父元素生成了BFC,从代码中可以看出他们的父元素时body,又从“根元素”可以生成BFC中似乎可以肯定应用这条布局规则是正确的,但有一点需要注意这里的“根元素”不是body,而是html,至于为什么,下一面再讲。
 
虽然上面这个效果图不可以完全用BFC来解释,但实现两栏自适应布局确可以完全用BFC来实现,根据规则d说明:
 
  “BFC的区域不会与float box重叠”
 
我们可以通过通过触发main生成BFC, 来实现自适应两栏布局。
main {
overflow: hidden;
}
当触发main生成BFC后,这个新的BFC不会与浮动的aside重叠。因此会根据包含块的宽度,和aside的宽度,自动变窄。效果如下:
 
 
2、清除浮动
 .par {
        border: 5px solid #fcc;
        width: 300px;
    }
 
    .child {
        border: 5px solid #f66;
        width:100px;
        height: 100px;
        float: left;
<div class="par">
        <div class="child"></div>
        <div class="child"></div>
    </div>
效果图:
根据BFC布局规则d:
 
  “计算BFC的高度时,浮动元素也参与计算”
 
为达到清除内部浮动,我们可以触发par生成BFC,那么par在计算高度时,par内部的浮动元素child也会参与计算。
代码:
.par {
    overflow: hidden;
}
效果图:
 
3、防止外边距折叠
p {
        color: #f55;
        background: #fcc;
        width: 200px;
        line-height: 100px;
        text-align:center;
        margin: 100px;
    }
<p>Haha</p>
    <p>Hehe</p>
效果图:
从图中可以看出,两个p元素的外边距发生了重叠,有资料用规则d来解释:
 
  “Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠”
 
这条规则生效的前提是根元素生成了BFC,但上文已经说过body元素默认是不生成BFC的,既然这样那又该如何解释呢?
 
根据W3C规范box model一节里Collapsing margins部分的说明,基本的意思是比邻元素的边距总是折叠,除了以下几种情况:
1、Margins of the root element's box do not collapse.(根元素不折叠)
2、If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins of following siblings but that resulting margin does not collapse with the bottom margin of the parent block.(有间隙不折叠)
 
1、Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).(浮动不折叠)
2、Margins of elements that establish new block formatting contexts (such as floats and elements with 'overflow' other than 'visible') do not collapse with their in-flow children.(创建BFC与子不折叠)
3、Margins of absolutely positioned boxes do not collapse (not even with their in-flow children).
Margins of inline-block boxes do not collapse (not even with their in-flow children).(positioned 不折叠)
4、Margins of inline-block boxes do not collapse (not even with their in-flow children).(inline-box 不折叠)
5、The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling, unless that sibling has clearance.(兄弟有间隙不折叠)
6、The top margin of an in-flow block element collapses with its first in-flow block-level child's top margin if the element has no top border, no top padding, and the child has no clearance.(父子间有padding 和 border 不折叠)
 
由此可见两个p元素发生折叠并不是因为BFC的缘故,虽然如此,我们依旧可以通过BFC的方法来解决折叠问题
我们可以在p外面包裹一层容器,并触发该容器生成一个BFC。那么两个P便不属于同一个BFC,就不会发生margin重叠了。
代码:
.wrap {
        overflow: hidden;
    }
    p {
        color: #f55;
        background: #fcc;
        width: 200px;
        line-height: 100px;
        text-align:center;
        margin: 100px;
    }
<p>Haha</p>
    <div class="wrap">
        <p>Hehe</p>
    </div>
效果图:
 
被忽略的部分
根元素产生BFC,这里的根元素默认不触发BFC,必须额外设置,很多资料上都未提及这个问题,看看下面的代码你就知道了
html,body{ margin:0px; }
html{ background-color: white; }
body{ background-color: green; }
.box{ width: 300px; height: 200px; margin-top: 100px; background-color: gray; }
<div class="box"></div>
效果图:
很明显这里的根元素并未能阻止外边距折叠,如果这个还不是很明显的话再来看看下一个
html,body{ margin:0px; }
html{ background-color: white; }
body{ background-color: green; }
.box{ width: 300px; height: 200px; background-color: gray; float: left; }
<div class="box"></div>
效果图:
根据BFC规则d
 
  “计算BFC的高度时,浮动元素也参与计算”
 
body的高度不应该是200px吗,怎么会是0
 
根元素什么情况下才会产生BFC呢?
经过测试发现,只有同时给html,body都加上overflow : hidden,才能触发BFC,又或者给body加上display : inline-block,display : table,position : absolute也可以触发BFC
方法一:
html,body{ margin:0px; }
html{ background-color: white; overflow: hidden;}
body{ background-color: green; overflow: hidden; }
.box{ width: 300px; height: 200px; background-color: gray; float: left; }

方法二:

html,body{ margin:0px; }
html{ background-color: white;}
body{ background-color: green; display:table/inline-block; /*position : absolute;*/ }
.box{ width: 300px; height: 200px; background-color: gray; float: left; }
 
为什么会这样呢,不是根元素会产生BFC吗?来看看规范是怎么说的
“Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.”
overflow:visible以外的块级元素将创建BFC,除非该值已经扩散到了视口。(大部分中文资料都忽略了这点)
 
再来看看overflow相关的标准:
UAs must apply the 'overflow' property set on the root element to the viewport.
1、When the root element is an HTML "HTML" element or an XHTML "html" element, and that element has an HTML "BODY" element or an XHTML "body" element as a child, user agents must instead apply the 'overflow' property from the first such child element to the viewport, if the value on the root element is 'visible'.
2、The 'visible' value when used for the viewport must be interpreted as 'auto'.
3、The element from which the value is propagated must have a used value for 'overflow' of 'visible'.
 
总结一下大概是这几点意思:
1、UA需要将root元素上的overflow属性置于视口之上;
2、overflow扩散行为:当root元素是html元素且overflow为visible,而且html元素有body作为其子元素,UA则需要将第一个body之上的overflow属性应用于视口;
3、用于视口的overflow: visible将被解析为overflow: auto
4、overflow扩散行为将导致body的使用值为overflow: visible
 
根据这几个规范,就可以解释根元素产生BFC了:
1、给body加上overflow:hidden,无法触发BFC创建。
解释:本用例中body {overflow:hidden},body的overflow:hidden被应用于视口,body的最终使用值为overflow:visible,因此body没有创建BFC。
2、给body和html同时加上overflow:hidden,成功触发BFC创建。
解释:本用例中body, html{overflow:hidden},html的overflow:hidden被用于视口,body的overflow计算值是hidden,因此创建了BFC。
3、给body加上display:table、display:inline-block、position:absolute,成功触发BFC创建。
解释:这些属性都导致body正常创建了BFC。
 
总结:
BFC大部分特性都可以用一句话来概括:
 
  “BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。”
 
因为BFC内部的元素和外部的元素绝对不会互相影响,因此, 当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠。同样的,当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度。避免margin重叠也是这样的一个道理。
 
本篇中反复提到外边距折叠的问题,这也是一个比较大的问题,不仅仅涉及到BFC,有空再出一篇文章。
 
参考资料:
https://www.w3.org/TR/CSS2/box.html#collapsing-margins
https://segmentfault.com/q/1010000002645174
http://www.cnblogs.com/lhb25/p/inside-block-formatting-ontext.html
http://www.cnblogs.com/pigtail/archive/2013/01/23/2871627.html
https://www.zhihu.com/question/35375980/answer/62492644
https://www.zhihu.com/question/35030317/answer/60872188
posted @ 2016-11-03 10:18  点点乐淘淘  阅读(929)  评论(0编辑  收藏  举报