BFC
更新日期:
目录:
一、BFC
二、IE 中的 hasLayout
三、BFC 和 hasLayout 的联系和区别
盒模型
Box(盒模型)是 CSS 布局的对象和基本单位,一个页面就是由多个 Box 组成的。Box 的类型要么是 Block,要么是 Inline,但不能同时属于这两者。在渲染页面的时候,每种 Box 都有它自己的 Formatting Context(格式化上下文):Block Box 在 Block Formatting Context 里格式化,Inline Box 在 Inline Formatting Context 里格式化。
也就是说,任何被渲染的元素都属于一个 Box,不是 Block 就是 Inline。即使是未被任何元素包裹的文本,根据不同的情况,也会属于匿名的 Block Box 或者 Inline Box。
简而言之,我们可以这么理解:所有的元素都被划分到了对应的 Formatting Context 里。 BFC 就是这里的 Block Formatting Context(块级格式化上下文)。
Box 类型
不同类型的 Box 会参与不同的 Formatting Context。
- Block-level Box 块级盒模型
display:block|list-item|table,会生成 Block-level Box,并参与 Block Formatting Context - Inline-level Box
display: inline|inline-block|inline-table,会生成 Inline-level Box,并参与 Inline Formatting Context - run-in box (CSS3才有)
注意:
display:block 会【参与】Block Formatting Context。此处的表述是“参与”,而并不是它自己就是 BFC。前者是一个 CSS 属性设置,后者是一个布局的上下文环境,两者是不同维度的东西。虽然,有时候一个 div 恰好就是一个独立的 BFC,但那也是因为另一个东西【让它成为了】一个不受外界干扰的世外桃源式的上下文环境。
Formatting Context
Formatting Context(格式化上下文)是一个决定如何渲染文档的容器。它是 W3C CSS2.1 规范中的概念。它代表页面中的一块渲染区域,并有一套自己的渲染规则。决定了其子元素如何定位,也决定了它和其他元素的关系和相互作用。
最常见的 Formatting Context 有两种:Block Formatting Context(BFC)和 Inline Formatting Context(IFC)。
CSS2.1 中只有 BFC 和 IFC。CSS3 中增加了 GFC 和 FFC。
BFC 定义
Block Formatting Context 直译:块级格式化上下文。它是一个独立的渲染区域,规定了内部的 Block-level Box 如何布局,并且与区域外部毫不相干。
BFC 布局规则
下面依次介绍 BFC 的6个布局规则。
内部Box垂直放置
规则1: 内部的 Box 会在垂直方向上一个接一个地放置
同BFC垂直margin重叠
规则2:Box 垂直方向的距离由 margin 决定,属于同一个 BFC 的两个相邻 Box 的垂直 margin 会发生重叠。
demo.rule1+2
因为:在普通流中,两个相邻的块框在垂直方向上的外边距会发生合并,即处于同一个BFC的两个块的垂直margin会重叠。
所以,在BFC布局中,就会出现一些看起来“诡异”的情况。来看下面的例子。
|
|
垂直外边距的表现:
1.两子之间的垂直外间距,是重叠的。两父之间也重叠。
2.当不给父设置外间距时,内部子伸出来的10px也能把两父撑开。即子的外间距,伸到了外边。
3.当给父也设置外间距时,父和子的垂直外边距,是重叠的。
原因:
因为这6个box是同属于一个BFC的(最外层的根元素),所以它们在垂直方向上不分你我的一个一个放,且外边距的逻辑也很单纯地就一个挨一个+垂直合并。
以上布局之所以如此,关键就在于:它们是在同一个BFC里的。
那么,当它们不在同一个BFC里时,情况会如何呢?我们给 .parent1 设置 overflow:hidden; 让 .parent1 变成一个独立的BFC。如下。
|
|
此处,我们重点关注与上面例子的不同之处:
1.父1内,父和子的垂直外间距的关系变了:
(1)父和子的垂直外间距不重叠了:因为它们目前不属于同一个BFC
(2)子的外边距,也不会伸出来了
原因:
父1被触发成独立的BFC后,它就是一个独立的渲染区域,它内部的布局是完全独立的-不受外界的任何影响。
父和子目前就不在同一个BFC里了,子所在的独立渲染区域是父div.parent1,而父所在的独立渲染区域是根元素html。
所以,它两是完全独立的,谁不影响谁,各渲染各。于是,垂直外间距也就不重叠了。
相同之处是:
1.父1内:两个子之间的垂直外间距,还和以前一样
2.父1和父2之间,本质上没发生什么变化。还是当初那两个平等的box
原因:这两个子依然是处于同一个BFC—只不过不是根元素了而是其父div。而两个父,也是依然处于同一个BFC—且还是之前的根元素,没变。
BFC起始包含孩子们
规则3:每个元素 margin box 的左边,与包含块 content box 的左边相接触。即时浮动也是如此。
若是从右至左渲染的,则是每个元素 margin box 的右边和包含块 content box 的右边相接触。
说明:
此处的包含块,是它的父BFC(上下文环境),而不是它的直接父元素(Dom结构层面)。
再次强调,BFC是个上下文布局环境,而父元素是结构上的。两者不是一个东东。
demo.rule3
div.parent1 有 10px 的内边距和外边距,div.child 有 10px 的外边距。以下是代码和UI:
|
|
三个 child 都是被包含在包含块的内容区域里的,即每个 .child 元素 margin box 的最外侧和包含块 content box 左侧对齐。
下面,我们把 .child1 改成左浮动,.child3 改成右浮动。如下:
|
|
说明:
- 左浮动的元素 .child1 和包含块 content box 的左边接触。
- 右浮动的元素 .child3 和包含块 content box 的右边接触。
- 正常的div元素,宽度是“满”的。其左侧临接包含块 content box 的左边,右侧临接包含块 content box 的右边。
与上例相比,除了由于左右浮动而导致的位置变动外,.child3 和 .child2 之间的间距也变了:之前是10px(它两的垂直外边距重叠),现在是20px(外间距没有重叠)。
原因:
.child3 设置了浮动之后,就触发了自己的 BFC — 致使它自己成了一个独立的渲染区域。故和 .child2 再也不会因为都是普通的Box而发生 margin 重叠了。
BFC不和float重叠
规则4:BFC 的区域不会和 float box 重叠
demo.rule4
继续上面的例子,我们给 .child2 设置 overflow:hidden 触发其 BFC。
|
|
此时,.child1和.child2的内容区域分开了,是因为.child2 此时也是独立的渲染区域,它不再和左浮动的.child1重叠了。至此,这三个child分别都是独立的BFC,彼此领土互不侵犯。
BFC独立容器
规则5:BFC 就是页面上一个隔离的独立容器。容器里的子元素不会影响到外面的元素
独立…独立到什么程度?— 不影响外面的元素…莫非是不论里面发生了什么惊天地泣鬼神的事情,外面的容器依然淡定的守在那里?
来,我们拿demo试试。
demo.rule5
首先,我们给.child1里放一对和容器等大小的猪猪,100*100px的。
|
|
好,现在猪猪长大了,变成了150*150px,来看看它的样子:
|
|
瞧,位置上丝毫没把 .child2 给“挤”挪挪,而是就这么赤裸裸地越界了。出了.child1 依然傲娇。为什么?因为我爹是BFC啊,独立有权。
再看 .child1 和 .parent 之间的关系,同样,高度上也潇洒地“溢”了出来。
—课间八卦2秒中—
至此,我们也可以看到,虽然BFC很独立自强自力更生,但它却在一定程度上破坏了网页本身的布局—让内容不自适应了。下面在“如何触发”小节里再详聊,此处先打个预防针。人无完人,布局也一样。
BFC的高度会计算浮动子
规则6:在计算 BFC 的高度时,浮动的子元素也会参与计算。
背景:
在 CSS2.1 中,高度的计算规则是:在普通流中,计算块级非替换元素的高度时,浮动子元素不参与计算。
所以,会经常导致一些看起来好像不符合预期的怪现象:子元素(子是float的)明明有高度,但父却 height:0。导致下方的内容冲上来了…我们来看下面的例子。
demo.rule6
|
|
现在,给 .parent 设置 overflow:hidden; 以触发其BFC。这时,父容器就被“撑”开了。
能解决什么问题?
上节结合实例,说明了 BFC 的布局规则,那么它能解决的问题也就比较明显了。此处简要概况下:
- 不和浮动的元素重叠 【规则4】 //最常见的是两栏自适应布局:左float触发BFC,右也触发BFC
- 清除内部浮动 【规则6】 //也比较常用
- 防止垂直 margin 合并 【规则2】 //主动用的比较少。无非是更能理解和解释 “为什么它两的垂直margin不合并了?!!”
以上这些所谓的能解决的问题,其实都是BFC本身的特性使然:因为BFC内部的元素和外部的元素不相互影响。
之前吧,自己对BFC的认识有个误区:最耳熟能详的是BFC能解决的那些问题,对它也仅仅停留在了概念的熟悉上而已。使用CSS两年,现在再重新梳理一遍后,就觉得:与其了解它能解决什么问题,倒不如直接问自己两个问题:“什么是BFC?”、“它有什么特性—是怎么布局的?”。能解决的问题仅仅是个表象,而“它之所以是它”才是此知识点的关键。
如何触发BFC?
- 根元素 html
- float:left|right //不是none的浮动元素
- position: absolute|fixed //绝对定位+fix定位,反正不同于正常流
- overflow不是visible的,overflow:hidden|scroll|auto
- display:inline-block|table-cell|table-caption|flex|inline-flex
说到可以触发 BFC 布局的属性们,此处又想提一点:每种触发属性都有其适用情况,要有理由地选择。因为不同属性都有自带技能,eg.
2.float:left|right; 会让元素脱离正常文档流,而失去元素本身自带的流体特性
3.position: absolute|fixed,同样,也会脱离正常文档流….而且还脱离的很彻底呢(同float相比的话)
4.overflow:hidden; 超出的内容会被截断…但它却能很好的保持元素本身的流体特性
5.display:inline-block; 虽然宽度能很好的自适应(相对float来讲-不用设置固定width),也能形成独立的渲染上下文。但它毕竟是宽度再也不“满格”了
这点,张鑫旭的博客 《CSS深入理解流体特性和BFC特性下多栏自适应布局》 中的 4.BFC元素家族与自适应布局面面观 里写的比较详细。感兴趣的同学,可以去拜读下。
综合考虑这几种方式自带的“副作用”,我们一般会选择最“无害”的方式。实际情况中使用最多的是这两个:
overflow:hidden;
display:inline-block; //ie6-7不支持
zoom: 1; //ie专属,ie特有的 hasLayout,下篇博客再聊这点
参考
BFC神奇背后的原理:http://melonh.com/css/2014/01/10/the-magic-bfc.html
BFC和IE的hasLayout:http://www.cnblogs.com/pigtail/archive/2013/01/23/2871627.html
CSS深入理解流体特性和BFC特性下多栏自适应布局:http://www.zhangxinxu.com/wordpress/tag/bfc/