BFC-Block Formatting Context

以前知道BFC,但是感觉理解的不是很清楚,今天查了些资料总结记录如下。

A Block Formatting Context is part of the visual CSS rendering of a web page in which block boxes are laid out. The positioning scheme to which it belongs is normal flow.

这句话告诉了我们,第一,BFC是可视化格式模型(Visual Formattting Model)的一个部分。第二,BFC属于普通流中的一种。

总的来说关系是:可视化格式模型>定位方案>普通流>BFC.


前提知识

在css的可视化格式模型(Visual Formattting Model)中有一个重要的概念:定位方案。

定位方案分为三种定位方案:

  1. 普通流(Normal Flow)
  2. 浮动(Floats)
  3. 绝对定位(Absolute Positioning)

普通流(Normal Flow)

在普通流中,元素按照其在 HTML 中的先后位置至上而下布局,在这个过程中,行内元素水平排列,直到当行被占满然后换行,块级元素则会被渲染为完整的一个新行, 除非另外指定,否则所有元素默认都是普通流定位,也可以说,普通流中元素的位置由该元素在 HTML 文档中的位置决定。


浮动(Floats)

在浮动布局中,元素首先按照普通流的位置出现,然后根据浮动的方向尽可能的向左边或右边偏移,其效果与印刷排版中的文本环绕相似。


绝对定位(Absolute Positioning)

在绝对定位布局中,元素会整体脱离普通流,因此绝对定位元素不会对其兄弟元素造成影响(如果看了上文的童鞋,会发现这点与浮动元素会影响兄弟元素是不同的),而元素具体的位置由绝对定位的坐标决定。

在这个普通流中,就有一种布局:BFC,它不会对兄弟元素造成什么影响。


那么到底什么事BFC呢?

从样式上看,具有 BFC 的元素与普通的容器没有什么区别。

但是从功能上,具有 BFC 的元素可以看作是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且 BFC 具有普通容器没有的一些特性,例如可以包含浮动元素,比如清除浮动的方法(如 overflow 方法)就是触发了浮动元素的父元素的 BFC ,使到它可以包含浮动元素,从而防止出现高度塌陷的问题。

简单来说,BFC 就是一种属性,这种属性会影响着元素的定位以及与其兄弟元素之间的相互作用

再简单的来说,用了下面的一些属性后,你就创建了一个BFC。这个BFC功能上不同于普通容器。

这个BFC并不是这些元素,而是某些元素的带有属性产生了BFC.


怎么样形成BFC

Floats, absolutely positioned elements, inline-blocks, table-cells, table-captions, and elements with ‘overflow’ other than ‘visible’ (except when that value has been propagated to the viewport) establish new block formatting contexts.

浮动、绝对定位元素(position 为 absolute 或 fixed)、行内块元素 display:inline-block 、表格单元格 display:table-cell 、表格标题 display:table-caption 以及overflow 属性值部位visible 的元素(除了该值被传播到视点viewport的情况)将创建一个新的块级格式化上下文。

也就是说下面的情况会形成一个BFC:

1. float的值不为none
2. position的值不为static或者relative
3. display的值为table-cell,table-caption,inline-block,flex,inlineflex
4. overflow的值不为visible

创建一个块级格式上下文可能产生的问题

一个BFC可以通过上面的很多css样式触发,但毕竟上面的CSS属性都有自己原本的样式,那么久可能在原来的基础上,加入了一些我们不需要的样子。比如说:

  1. display:table可能引发响应式问题
  2. overflow:scroll可能产生多余的滚动条
  3. float:left浮动,将把元素移动到左侧,并被其他元素环绕
  4. overflow:hidden讲剪裁溢出元素

所以,咱们在创建一个BFC的时候,就要选择最恰当的样式,一般可以选用overflow:hidden。

1
2
3
.container{
overflow:hidden;
}


那么BFC到底有很么特点和作用呢?


BFC中的盒子对齐

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).

在BFC上下文中,每个盒子的左外侧紧贴包含块的左侧(从右到左的格式里,则为盒子右外侧紧贴包含块右侧),甚至有浮动也是如此(尽管盒子里的行盒子Line Box可能由于浮动而变窄),除非盒子创建了一个新的BFC(在这种情况下盒子本身可能由于浮动而变窄)。

这是BFC的一个特点。简单来说,如上图所示,所以属于BFC的盒子都是左对齐(在从左到右的格式下)并且他们的左外侧紧贴包含块的左侧。在最后一个盒子中我们可以看到,尽管左侧存在一个浮动元素(棕色),另外一个元素(绿色)仍然紧贴包含块的左侧。该情况的产生原理将在下文关于文字环绕的部分中讨论。


使用BFC避免文字环绕

Using Block Formatting Contexts to Prevent Text Wrapping

浮动最初设计的目的就是文字环绕,这一个目的导致我们后来用浮动的时候会出现一些问题,比如说,我本就不想要文字环绕这一特点,我就需要想办法去除它。

1
2
3
4
5
6
7
8
9
10
<div class="container">
<div class="floated">
Floated div
</div>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
Dolorum rerum minima tenetur qui provident laudantium reprehenderit porro maiores facilis vitae, quas,
quos quibusdam eum accusamus suscipit. Quos molestiae saepe unde.
</p>
</div>

BFC

但事实是当我们把浮动的颜色去掉后是这样的
BFC
如我们所见,p 元素没有移位但它叠在了浮动元素之下,而p元素的行盒子(即文本行)却移位了,行盒子水平变窄来给浮动元素腾出了空间。

随着文本的增加,最后文本将环绕在浮动元素之下,因为那时候行盒子不再需要移位,也就成了图1的样子。这就是为什么即便有浮动元素,段落仍紧贴包含块的左侧,而行盒子会变窄来给浮动元素腾位子。

如果我们能位移整个 p 元素,这个环绕问题也就迎刃而解了。

然后我们再回顾下上一个BFC中的盒子对齐的特点。

在BFC上下文中,每个盒子的左外侧紧贴包含块的左侧(从右到左的格式里,则为盒子右外侧紧贴包含块右侧),甚至有浮动也是如此(尽管盒子里的行盒子 Line Box 可能由于浮动而变窄),除非盒子创建了一个新的BFC(在这种情况下盒子本身可能由于浮动而变窄)。

如果我们给p元素创建一个新的BFC,就可以解决了。我们给p元素加上overflow:hidden这个元素。
BFC
这样就变成了我们想要的样子了。

提醒,这也是为什么有的时候我们需要两块之间有一些间距,然后我们设置了margin-left但还是不管用的原因,并不是margin-left不管用,只是我们设置的太小啦。


会造成外边距重叠,但也可以防止外边距重叠

A Block Formatting Context Causes Collapsing Margins.

Block formatting contexts stop margins from collapsing.

仔细看上面两句话,是不是觉得有点奇怪,他的意思是说:BFC会造成外边距重叠,但是BFC也可以阻止外边距重叠。

在常规流中,盒子从包含块的顶部开始一个个地垂直摆放。两个同胞盒子间的垂直距离由两个盒子各自的外边距所决定,但不是二者外边距之和。

1
2
3
4
<div class="container">
<p>sibling 1</p>
<p>sibling 2</p>
</div>
1
2
3
4
5
6
7
8
.container {
background-color: red;
overflow: hidden;
}
p {
background-color: lightgreen;
margin: 10px 0;
}

BFC
上面我们利用overflow:hidden创建了一个BFC。这个BFC造成了外边距重叠,也就形成了上面的情况,本来该是20px的边距的,现在只有了10px。

在讨论了上面BFC折叠外边距的情况后,我们必须牢记于心的一件事是,相邻块级盒子(同胞)之间的垂直外边距只有在它们处于同一个BFC时才会发生折叠。如果它们分属于不同的BFC,就不会折叠了。所以,通过创建新的BFC我们可以避免外边距折叠

1
2
3
4
5
6
7
<div class="container">
<p>Sibling 1</p>
<p>Sibling 2</p>
<div class="newBFC">
<p>Sibling 3</p>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
.container {
background-color: red;
overflow: hidden; /* creates a block formatting context */
}
p {
margin: 10px 0;
background-color: lightgreen;
}
.newBFC {
overflow: hidden; /* creates new block formatting context */
}

BFC
由于第二个和第三个兄弟节点分属于不同的BFC,所以在他们之间不会有任何的外边距重叠。


使用BFC包含浮动

Using a Block Formatting Context to Contain Floats
BFC可以包含浮动。我们经常遇到容器中含有浮动元素的情况。这种情况下容器元素没有高度并且其浮动子元素脱离了网页的常规流。我们通常用清除浮动解决这个问题,最普遍的做法就是使用伪元素。但我们也可以通过创建一个BFC来解决问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="container">
<div>Sibling</div>
<div>Sibling</div>
</div>

.container {
background-color: green;
}
.container div {
float: left;
background-color: lightgreen;
margin: 10px;
}

BFC
上面的我们可以很清楚的知道为啥会没有把父元素撑起来,因为子元素浮动,脱离了文档流,我们一般用clear:both,或者伪元素,或者这里的overflow:hidden。都可以很好的解决这个问题。

1
2
3
4
5
6
7
8
9
10
.container {
overflow: hidden; /* creates block formatting context */
background-color: green;
}

.container div {
float: left;
background-color: lightgreen;
margin: 10px;
}

现在容器可以包住浮动子元素,并且其高度会扩展至包住其子元素,在这个新的BFC中浮动元素又回归到页面的常规流之中了。


在多列布局中使用BFC

Using Block Formatting Contexts in Multi-column Layouts

如果我们创建一个占满整个容器宽度的多列布局,在某些浏览器中最后一列有时候会掉到下一行。这可能是因为浏览器四舍五入了列宽从而所有列的总宽度会超出容器。但如果我们在多列布局中的最后一列里创建一个新的BFC,它将总是占据其他列先占位完毕后剩下的空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="container">
<div class="column">column 1</div>
<div class="column">column 2</div>
<div class="column">column 3</div>
</div>
.column {
width: 31.33%;
background-color: green;
float: left;
margin: 0 1%;
}
/* Establishing a new block formatting
context in the last column */
.column:last-child {
float: none;
overflow: hidden;
}

在尽管盒子的宽度稍有改变,但布局不会打破。当然,对多列布局来说这不一定是个好办法,但能避免最后一列下掉。这个问题上弹性盒或许是个更好的解决方案,但这个办法可以用来说明元素在这些环境下的行为。

BFC

为什么会是这样,这跟上面一个原因差不多。我们在最后定义了一个新的BFC后,这个盒子的左外侧就不会紧贴包含块的左侧。

当然,在这里我们也可以用增加伪元素来实现。


总结

主要是把BFC的整个的总结记录下。也方便以后查阅。学习参考自:understanding-block-formatting-contexts-in-css,感谢。