什么是BFC

477 查看

这是我10个月前写的一篇关于BFC的文章,因为接下来要写一篇关于FFC的文章了,然而很多人对BFC都还不了解,所以我先把这篇文章拿出来让大家先了解下BFC。

说到BFC,就会涉及到CSS布局的一些基本概念,然后,首先来讲一下关于块级盒的一些概念:

下面是来自w3.org中的一句话(参考链接:http://www.w3.org/TR/CSS2/visuren.html#block-formatting):

Block-level elements are those elements of the source document that are formatted visually as blocks (e.g., paragraphs). The following values of the ‘display’ property make an element block-level: ‘block’, ‘list-item’, and ‘table’.

大概的意思是块级元素是那种源文档被格式化为可视块了的元素,然后使这个元素变成块级元素的display属性取值如下: ‘block’, ‘list-item’, 和 ‘table’。

然后还有一段话:

Block-level boxes are boxes that participate in a block formatting context. Each block-level element generates a principal block-level box that contains descendant boxes and generated content and is also the box involved in any positioning scheme

块级盒block-level box是这种参与了块级排版上下文的一种盒子,每个块级元素都生成了一个包含后代盒子和生成的内容的主要块级盒,并且这个盒子参与了任何定位的计算

来自w3c官网的一张图:

Alt text

块级元素会自动生成一个块级盒block-level box,这是块级盒block-level-box的盒模型构成,它表明的是块级盒自身的结构构成。再来看w3.org的这样一段话( 参考:http://www.w3.org/TR/CSS2/box.html):

content edge or inner edge
The content edge surrounds the rectangle given by the width and height of the box, which often depend on the element’s rendered content. The four content edges define the box’s content box.
padding edge
The padding edge surrounds the box padding. If the padding has 0 width, the padding edge is the same as the content edge. The four padding edges define the box’s padding box.
border edge
The border edge surrounds the box’s border. If the border has 0 width, the border edge is the same as the padding edge. The four border edges define the box’s border box.
margin edge or outer edge
The margin edge surrounds the box margin. If the margin has 0 width, the margin edge is the same as the border edge. The four margin edges define the box’s margin box.

从上面这段话可以看出,margin、border、padding、content分别定义了元素四种边,然后每种类型的边的四条边定义了一个盒子,分别是content box、padding box、border box、margin box,而决定块盒在包含块中与相邻块盒的垂直间距的便是margin-box,这个margin-box是始终存在的,即使它的margin为0,试看下面的代码:

生成了一个块级的元素,同时元素也生成了一个块级盒:

Alt text

此时我没有为这个div设置margin值,但是这个div还是有一个默认为0的margin的,试看下面的浏览器生成的computed style:

Alt text

可以看到div默认的margin为0,再看看w3.org的相关文档(参考:http://www.w3.org/TR/WD-CSS2/visudet.html):

Alt text

上面说了元素的四个margin的默认值都是0(见红色部分),然后应用对象是所有的元素(见蓝色部分),所有元素当然包括块级元素,因此所有的块级盒子无论怎样都会有一个margin-box,在BFC中,margin-box会与其相邻的margin-box的对边相折叠(关于BFC的margin折叠这里先不讨论)。margin-box是参与块级盒在上下文中的布局的,但是参与BFC布局的盒子却是块级盒block-level box,并且还有一点需要明确的是参与布局的是盒子而不是元素本身。下面这张图是我自己理解的块级盒block-level box的构成:

Alt text

那么block-level box在页面中是怎样布局的呢。

我们通常的描述是这样的:

普通流中的块元素独占一行,然后从上往下一个接一个的排布,相邻元素间会有外边距折叠

确实是这样(NOTE:这个说法也非绝对严谨,试看DEMO:http://codepen.io/Dudy/pen/mywXKb,那为什么会这样呢,原因就是因为BFC的存在。试看下面的关于BFC的官方文档描述(参考:http://www.w3.org/TR/CSS21/visuren.html):

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.

In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the ‘margin’ properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.

In a block formatting context, each box’s left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box’s line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).

大致意思如下: 第一段:浮动元素、绝对定位元素,不是块级盒的块级包含块(比如inline-block、table-cell、table-capation)和overflow值不为visible的块级盒子为它们的内容建立了一个新的块级排版上下文。

第二段:在一个块级排版上下文中,盒子是从包含块顶部开始,垂直的一个接一个的排列的,相邻两个盒子之间的垂直的间距是被margin属性所决定的,在一个块级排版上下文中相邻的两个块级盒之间的垂直margin是折叠的。参与BFC的布局的只有普通流normal flow中的块级盒,而float、position值不为relative\static的元素是脱离BFC这一布局环境的,不参与BFC的布局

第三段:在一个块级排版上下文中,每个盒子的左外边是触碰到包含块的左边的(对于从右向左的排版,则相反),即使在有浮动元素参与的情况下也是如此(即使一个盒子的行盒是因为浮动而收缩了的),除非这个盒子新建了一个块级排版上下文(在某些情况下这个盒子自身会因为floats而变窄)。

先不解释第一段所描述的内容,先看第二段和第三段话,然后我们再来看一个很常见的实例:   代码如下:

结果如下:Alt text

三个块级盒,从上往下排列,看样子是遵循如上面第二段所述的这一条BFC布局规则的:“在一个块级排版上下文中,盒子是从包含块顶部开始,垂直的一个接一个的排列的”,那么这里的盒子要从上往下排列的话,肯定是得要一个包含块containing block和一个块级排版上下文BFC的,:root元素是会在其下创建一个BFC的,那么在这个BFC下的所有块级盒都是会在它的包含块中垂直的一个接一个的排布的。但是元素的包含块是什么呢,试看来自w3.org的一段话(参考:http://www.w3.org/TR/CSS2/visudet.html):

The position and size of an element’s box(es) are sometimes calculated relative to a certain rectangle, called the containing block of the element:

意思是:元素盒子的位置和尺寸往往是根据一个矩形计算出来的,我们称这个矩形为元素的包含块 然后它下面还有一句:

if the element’s position is ‘relative’ or ‘static’, the containing block is formed by the content edge of the nearest block container ancestor box.

意思是:如果这个元素的position值是relative或static,这个元素的包含块是由离其最近的块级的祖先盒子的内容的边content-edge构成的。就是离其最近的块级祖先盒子的content-box。

搞清了包含块containing block的概念,再来看看这个实例中的3个div的包含块,很显然是其父级元素body,同时还在canvas所设立的BFC下,按照“在一个块级排版上下文中,盒子是从包含块顶部开始,垂直的一个接一个的排列的”的这个定义,我们已经确定了,这里的div是会垂直的一个接一个的排列的,但是你要注意到,第二段话只是定义了垂直方向的排布规则,还没说水平方向的,那么水平方向的又如何呢,试看第三段话的前两句:“在一个块级排版上下文中,每个盒子的左外边是触碰到包含块的左边的(对于从右向左的排版,则相反)”,而在这里,我尚未为其定义从右向左的排版(对于从右向左的排版,参考:http://www.css88.com/book/css/properties/writing-modes/index.htm,所以这个div的左外边是会触碰到包含块body的左边的。

本文只讨论什么是BFC,因此上面那段话中的某些原文就不一 一解释了。

再来看上面那段话第二段中的一句话:“相邻两个盒子之间的垂直的间距是被margin属性所决定的,在一个块级排版上下文中相邻的两个块级盒之间的垂直margin是折叠的”,这是参与BFC布局的块级盒的又一特性,试看以下实例:

代码:

如图:

alt text

可以看到上下两个div的margin折叠了,第一个div的margin-bottom和第二个div的margin-top折叠为了30px,第二个div的margin-bottom和第三个div的margin-top折叠为了50px,而这个折叠本质是块级盒block-level box下的margin-box的折叠,后面会讲到如果再到这几个div的外面一层再包裹一个拥有BFC的元素的话,他们之间的margin便不会折叠了,因为BFC里面的盒子和其外面的盒子间是不会有任何影响的,你可能会疑惑那这里的三个div不是也在:root所设立的BFC下吗,那为什么还会折叠,原因很简单,就是因为你那个BFC在:root那里去了,BFC相当于一堵墙,你这个墙在这里应该在每个div的外面才会起到隔离这几个div的作用啊,而:root下的那个BFC则是隔离的:root下的直接子元素了。关于margin折叠方面的还有很多细节,可以参考官方文档:http://www.w3.org/TR/CSS2/box.html#collapsing-margins

至此,我们就搞清楚普通流中的块级盒的一些基本特性的来龙去脉了。

上面说的都是:root下的BFC。那么,假设你在不知道比如float的这些特性能用于创建BFC的时候,你会不会很好奇的去想:root下的盒子会不会也有可以用于创建BFC的方法与对应的盒子呢?上下文套上下文可是一件很令人感到愉悦的事情,因为我可以把那个能创建BFC的盒子当作那个:root,而这个创建了BFC的盒子则是一个独立的容器,里面的参与BFC的块级盒不会影响到盒子外面的盒子,外面的盒子也不会影响到里面参与了BFC的块级盒。试看以下demo: &nbsp:;&nbsp:;代码如下:

&nbsp:;&nbsp:;结果如下:

Alt text

可以看到,上面示例中的上面灰色div和下面黑色div的margin并没有重叠,这是因为那两个float的父盒子在为它下面的盒子创建了一个BFC,从而将float盒子里面的子盒子给隔离了起来,因此也就不会margin折叠了,这只是创建BFC的一个方法,其它的还有overflow:hidden等,而在这个BFC下的盒子都是遵循BFC的布局规则的。

然后我们来从盒模型的角度来理解下,BFC是如何将其下的盒子与外界隔离起来的,首先,最基本的盒子构成我们上面已经说过了,见上面的描述block-level box的构成的那张图:

然后,当块级盒block-level box外层没有BFC作保护,有margin折叠时,是这样的:

Alt text

而当块级盒block-level box外层有BFC作保护时(比如给下图灰色边框线盒子一个float:left),则是这样的:

Alt text

同时BFC下的盒子是按照BFC的规则从上往下一个接一个的排列,并且存在外边距折叠的,你可以通过在这层BFC下再去嵌套BFC来阻止下面盒子的外边距折叠

Alt text

最后,我们通过一张图来了解一个页面中的BFC的构成(有红色虚线的代表的是拥有BFC的元素):

Alt text

BFC描述的是页面中的,严格来说是页面的:root下的一个布局上下文,它下面的盒子遵循BFC中的布局规则,它是描述的是块级盒的布局规则,那么行盒,行级盒等盒子又是遵循什么样的布局规则的呢?这里就不细讲了,可以看看winter老师的两张图,有助于对BFC的理解,也有助于对CSS布局的理解:

Alt text

Alt text

最后要说的就是,本文只对BFC作了一个初步的介绍,如果要了解更多的细节,可以参考以下相关标准:

http://www.w3.org/TR/CSS2/box.html
http://www.w3.org/TR/CSS2/visuren.html#block-formatting
http://www.w3.org/TR/CSS2/box.html#collapsing-margins