前几天看到kejun‘s blog里面的一篇文章:“近期面试感受”, 里面提到面试时的一道题的不同的解决方案,比较它们之间的优缺点,其中出现了一个概念:Block Formatting Contexts(块级格式化上下文),而我一点印象都没有,所以就找了一些相关的文章学习一下,看到一篇很好的文章,所以就翻译出来和大家共享一下。原 文是:Block Formatting Contexts
一个块级格式化上下文是满足以下条件中至少一条的盒模型:
- float的值不为空
- overflow的值不为visible
- display的值是table-cell、table-caption或者inline-block之一
- position的值不为static或者relative
当谈到视觉格式模型(visual formatting model,针对可视化媒体,用户代理如何处理DOM树),块级格式化上下文发挥着巨大的作用。所以对于CSS编程人员来说,深刻的理解块级格式化上下文和flow、float、clear、margin的关系至关重要。
先看一下有关BFC的规则或者定义
块级格式化上下文如何浮动
块级格式化上下文的定位方案(positioning scheme)属于普通流(normal flow)。块级格式化上下文中的块是通过页面流中的块级元素、行内元素的行内格式化、块级元素或者行内元素的相对定位以及将元素定位为run-in来定位的。简言之,块级格式化上下文就是页面流的一部分。
怎样触发块级格式化上下文
以下方法可以创建新的块级格式化上下文:
- floats
- 元素的绝对定位
- inline-blocks
- table-cells
- table-captions
- 给元素添加overflow样式,并且值不为visible
但是根据CSS3标准,当以下条件符合的时候就会创建块级格式化上下文:
position的值不是static或者relative(见【CSS3POS】)
这个定义意味着当position为fixed的时候也会创建新的块级格式化上下文。参照标准,一个绝对定位的元素就暗示着这个元素的position属性值为absolute或者fixed。
请注意display:table本身不会创建块级格式化上下文。但是由于它可以产生匿名盒(anonymous boxes),而匿名盒中的display:table-cell可以创建新的块级格式化上下文。换句话说,触发块级格式化上下文的是匿名盒,而不是 display:table。编程人员需要铭记于心的是,虽然这两种样式都能够创建新的块级格式化上下文(间接的或者直接的),但是 display:table的clear却和display:table-cell的clear效果不一样。
最后一个触发块级格式化上下文的是fieldset元素。奇怪的是,www.w3.org里没有任何有关这个触发行为的信息,直到HTML5标准里才出现。有些浏览器bugs(Webkit,Mozilla)提到过这个触发行为,但是没有任何官方声明。实际上,即使fieldset在大多数的浏览器上都能创建新的块级格式化上下文,开发者也不应该把这当做理所当然的:
CSS 2.1没有定义哪种属性适用于表单控件和相框,也没有定义如何使用CSS来给它 们添加样式。用户代理可能会给这些属性应用CSS属性,建议开发者们把这种支撑 当做实验性的。更高版本的CSS可能会进一步规定这个。
块级格式化上下文都做些什么
由于块级格式化上下文的流动方式,所以它包含了浮动,这样可以阻止外边距叠加和浮动重叠:
在块级格式化上下文中,盒(boxes)都是从一个包含块的顶部开始,垂直的一个接一个排列着。两个兄弟元素之间的垂直距离由margin属性来决定的。在块级格式化上下文中,相邻的块元素之间的垂直外边距会产生叠加。
在块级格式化上下文中,每个框(box)的左外边缘挨着包含块的左边(如果是从右往左格式化,情况正好相反)。哪怕存在浮动也会如此,除非这个框(box)创建了一个新的块级格式化上下文(这种情况下,这个框会由于浮动而变窄)。
说了这么多规则之后,在现实世界中这意味着什么?
块级格式化上下文或多或少有点像其他的块框(block box),除了下面这些例外:
块级格式化上下文会阻止外边距叠加
仅当两个相邻的块框在同一个块级格式化上下文中时,它们之间垂直方向的外边距才会叠加。换句话说,如果这两个相邻的块框不属于同一个块级格式化上下文,那么它们的外边距就不会叠加。
例如:
This is a paragraph inside a DIV with a blue background, styled with margin:20px
This is a paragraph inside a DIV with a blue background, styled with margin:20px.
在上面的前两个框中,第一个框的底部外边距和第二个框的顶部外边距叠加在了一起(它们之间的间距是20px而不是40px),但是由于最后一个 DIV创建了一个新的块级格式化上下文,第三段的外边距并没有叠加,因此外边距没有超出段落所在的容器,而是成为了这个块框的一部分。
注意:在IE6中,如果没有明确的margin值,DIV将无法附上margin属性。
当谈到外边距叠加时,创建一个新的块级格式化上下文的作用是和给这个元素添加border或者padding属性是一样的。
块级格式化上下文包含浮动
我相信你应该听过这句话:“ a float always contains floats”,或者可能听过FNE方法。但是,这个方法的基础则是浮动是块级格式化上下文,所以用一种比较好的方式来表达这个就是“块级格式化上下文通常包含浮动”。
例如:
This paragraph is a float inside a DIV with a blue background, it is styled with margin:20px.
第一个段落是个浮动的段落,所以它从流中移除了并且它所在的容器叠加在了一起,所以它所在的容器背景并没有显示出来。
第二个段落也是一个浮动的段落,但是它的内部包含了一个可以创建新的块级格式化上下文的DIV,因此这个容器附上了它子节点的margin属性。你 应该注意到,和第一个段落不一样的是,没有必要来清除前一个框的浮动。通常把这个称作“自我清除”(self-clearing),考虑到块级格式化上下 文是普通流的一部分,self-clearing将非常有意思。
注意:clear仅仅清除同一个块级格式化上下文里的浮动。
块级格式化上下文不会重叠浮动元素
这个是我最喜欢的。根据规定,一个块级格式化上下文的边框不能和它里 面的元素的外边距重叠。这就意味着浏览器将会给块级格式化上下文创建隐式的外边距来阻止它和浮动元素的外边距叠加。由于这个原因,当给一个挨着浮动的块级 格式化上下文添加负的外边距时将会不起作用(Webkit和IE6在这点上有一个问题——可以看这个测试用例)。
例如:
.sideBar { background: skyBlue; float: left; width: 180px; }
.sideBar { background: yellow; float: right; width: 180px; }
由于这个行为依附于有边框的框(border box)而不是有外边距的框,为了创建粉红色框和它兄弟框的间距(例如一个20px的间距),编程人员需要做下面两件事情中的任何一个:
- 给浮动元素设置一个20px的外边距
- 将粉红色框的外边距设置的比浮动元素的宽度大一点(例如:margin:0 220px)
是的,你会使用220px而不是20px。因为是有边框的框要试着处在两个浮动元素之间而不是有外边距的框。上句中的“试着”是因为如果这两个浮动元素之间没有足够的空间,那么这个容器就要下沉。
换句话说,如果粉红色的框设置了一个400px的宽度,那么当它的父容器小于770px(180px+180px+400px+10px)时,这个 框就会下沉。在少数情况下,在Firefox(至少在v.3.5.9中)中,这种行为将会被打破(例如,当上面的这个结构是body元素的第一个子节点时 —看测试用例)。
注意:在IE6中,粉红色框和两个浮动元素之间的显示的空间是由于IE中的3像素文本偏移的bug。
hasLayout和BFC
正如你已经注意到的,前面的所有的例子都用了样式:overflow:hidden;zoom:1。第一个声明是为了在现代浏览器中创建一个块级格式化上下文,而第二个声明则是为了触发IE(IE5.5/6/7)的hasLayout。这是因为那些渲染都很相近(CSS规则的相似处)。就像块级格式化上下文一样,给元素一个布局似乎可以防止外边距叠加,也可以包含浮动和防止叠加浮动。
可以给元素添加布局的属性或者声明
下面的清单显示了创建一个新的块级格式化上下文的同时也触发hasLayout的属性,至少这些属性会被浏览器支持,除了IE6或更低版本不支持overflow。
在IE5和6中:
- position:absolute
- position:fixed
- float:除了none的任何值
- display:inline-block
- width:除了outo之外的任何值
- height:除了auto之外的任何值
- zoom:除了normal之外的任何值
- writing-mode:tb-rl
- -ms-writing-mode:tb-rl
在IE7中:
- min-width:任何值
- min-height:任何值
- max-width:除了none之外的任何值
- max-height:除了none之外的任何值
- 添加了overflow样式的元素,并且overflow的值不为visible
- overflow-x、overflow-y:除了visible之外的任何值
需要考虑的事:
- zoom和writing-mode是IE专有的属性,并且无法通过验证。
- IE5.0不支持zoom
- 仅当width和height属性应用到行内元素时才会触发hasLayout(例如在IE6怪异模式下)
- overflow-x和overflow-y是CSS盒模型模块的一部分
- 当布局流和父布局流不同时也会触发hasLayout
在怪异模式和IE7模式中
- 当overflow设置的值不为visible时,table-cell元素不会创建新的块级格式化上下文
- 当overflow的值为visible时,table-cell元素会创建一个新的块级格式化上下文。
HTML元素通常都会有布局:
在IE中,下面的元素默认情况下会有一个布局:
- <body>(就像严格模式里的<html>一样)
- <table>,<tr>,<th>,<td>
- <img>
- <hr>
- <input>,<button>,<select>,<textarea>,<fieldset>,<legend>
- <iframe>,<embed>,<object>,<applet>
- <marquee>
总结
为了减少现代浏览器和IE(<8)之间的问题,编程人员可以给框(box)一个布局来创建新的块级格式化上下文。在这种情况下,流式相同的, 元素也是通过相同的方式清除浮动,clear也是清除相同的浮动,外边距也像预期的那样叠加。同时,编程人员在通过触发hasLayout的方式来给框添 加样式时必须留意因为这种添加样式的方法可能使那个元素变成新的块级格式化上下文。