浅谈BFC

BFC是什么?为啥要知道这个东西?今天我们来搞搞清楚。
首先,我们得弄清楚“视觉格式化模型”(visual formatting model)这个东西。要弄清楚这个,我们得先说说浏览器的渲染原理了。
http://taligarsiel.com/Projects/howbrowserswork1.htm
这是我很喜欢的一篇阐述浏览器运行原理的说明类文章,其中有一章提到了CSS的视觉模型(visual model)。根据其中的讲述,浏览器是在画板中绘制“格式化文档结构”的,而在绘制的过程中,浏览器会为每一个元素节点都创建一个矩形空间并在其中放置文档树的内容,这也就是大名鼎鼎的css盒模型,而其放置元素的依据就是视觉格式化模型。
话说,这些被创建的盒子都有一个叫做display的属性,来表明它到底是啥样的盒子。也就是说,根据display的值,元素会对应不同类型的controlling box。而针对不同类型的盒子,视觉格式化模型会反馈出不同的显示机制。盒子的类型大概有:
块盒(block box):
当一个盒子的display属性被设置为block,list-item或 table时,会生成block-level box
行内盒(inline box):
当一个盒子的display属性被设置为inline,inline-block或 inline-table,会生成inline-level box
匿名盒(anonymous box):
不能利用选择器来选择的盒子,所以它们的属性为:inherit或默认初始值
那么如上所述,BFC指的就是所谓的“块盒”所参与到的块级格式化过程中。同理可以类推,行内盒参与到的则是行内格式化上下文的过程,成为IFC。要注意,只有block-level box才能参与到BFC的过程中,是只有哦。同样,IFC也是同样的道理。
关于FC,再多说两句。每个盒子都有且仅有一个FC值,简单理解,不同的FC值代表一组盒子不同的排列方式。BFC值表示盒子从上到下垂直排列,而IFC则是表示盒子从左到右的水平排列方式。inline-level box的FC特性值固定为IFC。
每个BFC都有自己独特的作用域范围,并不互相影响。可以把它理解为一个容器,元素在其中按照自己的规则进行排列展示,但其他的BFC却不感知。
如此,相信大家已经理解了BFC产生的原因了。那么,到底哪些条件会触发BFC呢?整理一下从各方搜集来的资料:

  • body根元素或其它包含它的元素;
  • 浮动 (元素的float不为none,脱离文档流);
  • 绝对定位元素 (元素的position为absolute或fixed,脱离文档流);
  • 行内块 inline-blocks(元素的 display: inline-block,具有block的特征);
  • 表格单元格(元素的display: table-cell,HTML表格单元格默认属性);
  • overflow的值不为visible的元素 (hidden、auto、scroll);
  • 弹性盒 flex boxes (元素的display: flex或inline-flex);
从这里可以看出,凡事具有块级元素特征的盒子,或者是脱离文档流的元素都属于BFC,同时,我们熟悉的overflow属性也可以创建BFC。
BFC具有自己独特的布局规则。也整理一下:
  • 内部的盒子会在垂直方向一个个地放置
  • 属于同一个BFC的两个相邻Box的上下margin会发生重叠,与方向无关
  • 每个元素的左边,与包含的盒子的左边相接触(对于从左往右的格式化,否则相反),即使存在浮动也是如此
  • BFC的区域不会与float的元素区域重叠
  • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面元素,反之亦然
  • 计算BFC的高度时,浮动元素也参与计算
好了,概念性的东西到此为止。到目前为止,我们理解了BFC是如何形成的、如何触发的以及如何布局的,那么当遇到实际的布局问题的时候,我们同样也可以根据BFC的原理来对元素布局进行调整,从而达到想要的布局效果。
首先举一个最常见的相邻Box上下margin重叠的例子吧:
.p { width:200px; height:50px; margin:50px 0; background-color:red; }

我们可以分析一下上面的这个简单例子。首先,body根元素触发了BFC,所以其中包含的两个元素P属于该BFC下的相邻Box,这时候对其赋值margin属性,会命中相应的布局规则。因此,最终的布局效果是这样的:
浅谈BFC
文章图片
那么,如果要改进这种布局的话,只需要让两个div从属于不同的BFC即可。实现的方式比较多:
方法一:

.warp{ overflow: hidden; } .p{……}

方法二:

.p2{ float: left; }

方法三:

.p2{ display: inline-block; }

其实,本质上都是一样的,给body的子元素创建一个BFC,从而避免margin的折叠。
浅谈BFC
文章图片
再举一个清除浮动覆盖的例子。

.aside { width: 100px; height: 150px; float: left; background: #f66; }.main { height: 200px; width: 200px; background: #fcc; }

上述代码会导致浮动内容对主内容的覆盖。
浅谈BFC
文章图片
所以,如果对BFC有了了解,我们就可以得知,产生覆盖的原因是,浮动的内容创建了新的BFC,导致其左边与包含的盒子的左边重合,从而导致了内容覆盖。因此,清除覆盖的办法也很简单,对其覆盖元素创建另外的BFC就可以避免这个现象。

.aside { width: 100px; height: 150px; float: left; background: #f66; }.main { …… overflow: hidden; //其他任何触发BFC的条件都可以 }

效果如下:
浅谈BFC
文章图片
那么,为什么创建为新的BFC的main没有也贴到包含盒子的左边呢?当然是因为BFC区域不与浮动元素相重叠啦~
最后一个简单的例子,利用BFC清除浮动
overflow:hidden; .parent {border: 5px solid #fcc; width: 300px; }.child {border: 5px solid #f66; width:100px; height: 100px; float: left; }

如此,表象为:
浅谈BFC
文章图片
所以,如果想让内容把这个区域撑开,我们可以通过在parent元素上设置BFC来实现。
.parent {overflow: hidden; //其他可以创建BFC的属性均可 }

浅谈BFC
文章图片
在这个例子中,值得注意的是,DOM中存在一个匿名盒子“overflow:hidden; ”,而这个盒子的display属性会继承父元素的该属性,所以,对parent元素的不同的BFC设置会影响到该行文字的显示位置。比如:
.parent { display: flex; }

浅谈BFC
文章图片
【浅谈BFC】好了,就说这么多。有空我们再说说IFC,甚至是GFC、FFC吧!

    推荐阅读