常规流之块级格式化上下文(Block Formatting Contexts)
在css2.1中,常规流包括块框(block boxes)的块格式化(block formatting),行内框(inline boxes)的行内格式化(inline formatting),块级框(block-level-boxes)或行内级框(inline-level-boxes)的相对定位。常规流中的框属于一个格式化上下文,可能是块或者是行内,但不能同时都是。块级框参与块级格式化上下文,行内级框参与行内级格式化上下文。今天我们先来说说块级格式化上下文,也就是我们常说的BFC。
一.形成块级格式化上下文
- 绝对定位元素(fixed其实是absolute的一个子集)
- display为inline-block,table-cell,table-caption,flex,inline-flex(这里有一点要注意的,display-table本身不会形成BFC,但是它会产生匿名框,其中包含的dispaly:table-cell元素会形成BFC)
- overflow不为visible
- 根元素
- float属性不为none
这里要说明的是这些是形成块格式化上下文,而不是说是参与块级格式化上下文,这两个概念很容易弄混,大家可以仔细体会下。块格式化上下文是一个独立的渲染区域,而且只会有块级框(block-level box)来参与,它规定内部的块级框如何布局,与这个区域的外部毫不相干。
二.块格式化上下文中的特性
在块格式化上下文中,框会一个接一个的被垂直放置,他们的起点是一个包含块的顶部。
两个兄弟框之间的垂直距离取决于margin属性。在BFC中相邻的块级元素的垂直边距会折叠。
在BFC中,每一个元素的左外边(left margin边界)与包含块的左边相接触(对于从右到左的格式化,右边距接触包含块右边)。即使存在浮动也是如此(尽管一个元素的内容区域会由于浮动而压缩),除非这个元素也形成了一个新的BFC。
计算BFC高度的时候,浮动子元素也会参与计算。
BFC的区域不会与float box重叠。
三.块级格式化上下文的作用
我们之所以要了解一些东西,无非是需要有现实的意义和作用,不然谁管它(笑)。
那么BFC有什么作用呢,那就得从BFC的特性说起了。下面,我们会根据一些具体的例子来一个个说明。
1.清除浮动
我们上面说了,计算BFC高度的时候,浮动子元素也会参与计算,而浮动子元素造成的塌陷想必大家也都非常了解,那么如果父元素形成了BFC,是不是就可以包含浮动子元素了呢?直接看例子(为了直观我就不贴可运行代码了):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>BFC清除浮动</title> <style> *{margin:0; padding: 0;} .wrap-wrap{ height: 150px;} .wrap{ width: 300px; background-color :#fcc; color: #fff;} .son1,.son2{ float: left; width: 100px; height: 100px; background-color:red;} .son2{ background-color:blue; } .wrap-bfc{ overflow: hidden;} </style> </head> <body> <div class="wrap-wrap"> <div class="wrap"> wrap <div class="son1">son1</div> <div class="son2">son2</div> </div> </div> <div class="wrap wrap-bfc"> wrap <div class="son1">son1</div> <div class="son2">son2</div> </div> </body> </html>
运行结果如下:
我们通过一个overflow:hidden使下面的wrap形成了BFC,它高度的计算中浮动子元素也会参与,自然就包含了浮动元素,从而达到了清除浮动的目的。当然,不止overflow:hidden可以清除浮动,各位可以自行试试其他属性。
2.防止垂直margin重叠
在同一BFC中相邻的块级元素的垂直边距会折叠。那么如果这两个元素不在一个BFC中了会发生什么呢,继续看例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>防止边距折叠</title> <style> *{margin:0; padding: 0;} .wrap,.wrap1,.wrap2{ width: 200px; height: 100px; background-color :#fcc; color: #fff;} .son1,.son2{ width: 50px; height: 20px; margin: 10px; background-color:red;} .son2{ background-color:blue; } .wrap1{ overflow: hidden; background-color:lime; } .wrap2{ overflow: hidden; background-color:lime; } .new-wrap{ overflow: hidden;} </style> </head> <body> <div class="wrap"> <div class="son1">son1</div> <div class="son2">son2</div> wrap </div> <p>——————分割符————</p> <div class="wrap1"> <div class="son1">son1</div> <div class="son2">son2</div> wrap1 </div> <p>——————分割符————</p> <div class="wrap2"> <div class="son1">son1</div> <div class="new-wrap"> <div class="son2">son2</div> </div> wrap2 </div> </body> </html>
运行结果如下:
这里有两点要说。
1).wrap1与son1的margin折叠
我们先看wrap,son1是有个margin-top,但是图中给我们的感觉是没有,反而是它的父元素wrap有一个margin-top。为什么呢,这个就要看magrin折叠的说明了,这个也是标准中的内容:
In this specification, the expression collapsing margins means that adjoining margins (no non-empty content, padding or border areas or clearance separate them) of two or more boxes (which may be next to one another or nested) combine to form a single margin. 所有毗邻的两个或更多盒元素的margin将会合并为一个margin共享之。毗邻的定义为:同级或者嵌套的盒元素,并且它们之间没有非空内容、Padding或Border分隔。
可知嵌套的盒元素也会Collapsing Margins,就不难知道为什么会这样了。而在wrap1中,同样的结构,也没有非空内容、Padding或Border分隔,为什么又不折叠了呢?这里就要知道什么叫毗邻了:
Two margins are adjoining if and only if:
- both belong to in-flow block-level boxes that participate in the same block formatting context
- no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.)
- both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
- top margin of a box and top margin of its first in-flow child
- bottom margin of box and top margin of its next in-flow following sibling
- bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height
- top and bottom margins of a box that does not establish a new block formatting context and that has zero computed 'min-height', zero or 'auto' computed 'height', and no in-flow children
我就不一个个翻译了,就看第一条,这些margin都处于普通流中,并在同一个BFC中。我自己的理解是下面这样的,不一定正确,如果知道的同学还请指正:
wrap1的overflow:hidden属性形成了一个新的BFC,son1就参与了这个新的BFC。而wrap1本身是在根元素形成的BFC种,由此两个就不在同一个BFC中了,也就不是毗邻的了,自然margin不会折叠。
2).son1与son2的边距折叠。
根据上面一点说的,son1和son2是满足margin折叠的条件的,那么如果我们给son2包裹个new-wrap,并让它形成BFC,自然就没有边距折叠了。
3.自适应两栏布局。
这个直接上代码吧,根据例子来说,比较直观
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>自适应布局</title> <style> *{margin:0; padding: 0;} body { position: relative; color: #fff; } .aside { width: 100px; height: 150px; float: left; background: #f66; } .main { height: 200px; background: #fcc; } </style> </head> <body> <div class="aside">float aside</div> <div class="main">main</div> </body> </html>
运行结果:
这里首先要说的是main的位置问题。在BFC中,每一个元素的左外边(left margin边界)与包含块的左边相接触(对于从右到左的格式化,右边距接触包含块右边),这里虽然有float的aside,但是main的左边依然与包含块的左边接触。
而如果我们对main设置这样的样式:main:overflow:hidden,结果如下:
当触发main生成BFC后,这个新的BFC不会与浮动的aside重叠。因此会根据包含块的宽度,和aside的宽度,自动变窄。为什么呢:
BFC的区域不会与float box重叠。
四.总结
今天我们主要分析了什么是BFC、怎么形成BFC以及BFC的作用。其中有一些我总结个人的理解,希望对大家有帮助。另外,在css3的草案中,对这个概念做了改动,将Block formatting context 叫做 flow root,触发方式也发生了变化:The value of 'position' is neither "static" nor "relative".fixed作为absolute的一个子类也会形成flow root,有兴趣的同学可以自己去了解下。好了,就这么多了,我写东西都是准备好了然后一口气,后面再一点点修改,初成文难免会有错漏,还请大家多多指正。下一章会分析下行内级格式化上下文的相关。
参考:
2.w3c盒模型
3.w3c常规流