理解BFC,不妨看看这篇

330 阅读7分钟

什么是BFC?

BFC是block formatting contexts的简称,中文含义「块级格式化上下文」。我们先来看一下MDN对它的定义

BFC是Web页面的可视CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域

一看这定义,这也太抽象了吧,看完还是一脸懵有木有?

还是来撸段代码来压压懵吧

<div class="outer">
  <div class="inner">
    我是内部元素
  </div>
  我是一段文字,我是一段文字,我是一段文字,我是一段文字,
  我是一段文字,我是一段文字,我是一段文字,我是一段文字,
  我是一段文字,我是一段文字,我是一段文字,我是一段文字,
  我是一段文字,我是一段文字,我是一段文字,我是一段文字,
  我是一段文字,我是一段文字,我是一段文字,我是一段文字,
  我是一段文字,我是一段文字,我是一段文字,我是一段文字,
</div>
<style>
.outer {
    width: 500px;
    border: 5px solid red;
    padding: 10px;
 }
 .inner {
    background-color: pink;
    border: 3px solid blue;
    color: white;
    float: left;
    width: 300px;
   	height: 200px;
  }
</style>

效果是这样的

然后留一行文字删除其他所有的文字,效果是这样的

内部元素已经超出父元素的范围了,why?这是float属性造成的父元素高度塌陷,现在父元素的高度是文本内容所占据的高度加上padding和border的高度,那么该怎么解决这个问题呢?第一印象,这个是由于浮动造成的,那么我应该清除浮动clear:both, 那么在不添加任何标签的情况下貌似没法儿添加清除浮动。这里可以利用一下CSS的伪元素,需要添加以下代码

.outer::after {
    visibility: hidden;
    display: block;
    content: "";
    clear: both;
    height: 0;
}

通过上面的CSS其实原理就是添加了一个元素,然后加上了清除浮动。虽然这个能达到想要的效果,感觉有点麻烦,毕竟需要写这么多行,这还怎么提高生产力。说到这儿,就祭出我们的大杀器了,我们只需要在.outer里添加一行overflow:hidden或者overflow:scroll等等,只要overflow的值不是visible, 你想用哪个就用哪个,哪个熟悉就用哪个。添加之后的效果如下

这里我们不得不又问一个why了?这种解决方式才是我们今天的主题BFC,当我们设置overflow:hidden的时候,其实是创建了一个BFC。整个父元素作为一个BFC,也就是一个独立的渲染区域,区域内的父元素自然会包含所有的子元素,所以父元素也就包含了浮动的元素。

我记得当时我写css的时候就是各种尝试,当明白原理之后就会是轻车熟路了。

创建BFC

上面的一个例子我们通过创建一个新的BFC实现了我们想要的结果,那么创建BFC的方式有哪些呢?我们还是先看一下官方的原话

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.

中文大概的意思就是下面四条:

  • Floats 浮动
  • 绝对定位的元素 position:absoulte
  • 是块容器(block containers)但不是块级盒子(block boxes)inline-blocks, table-cells, table-captions
  • overflow不是visible

所以我们上面那个例子就是利用第四条规则创建了新的BFC。

BFC布局规则

关于BFC的布局规则,这里还是先引入W3C CSS2的原话

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

翻译过来大概意思就是下面的四句

  1. 在BFC里盒子会一个接着一个从上到下顺序排布
  2. 两个相邻的盒子的垂直间距使用margin属性
  3. 两个块级盒子的垂直margin会发生折叠
  4. 每个盒的左外边缘都会接触包含块的左边缘(如果是从右往左的格式,则是接触到右边缘),这个在float中也会存在。

第一条相信你已经很熟悉了,BFC里面都是块级的盒子,所以正常排列也就是从上至下排布了。

第二条也是很熟悉的属性了,那就是margin的作用。只要是与其他元素的间距我们都使用margin属性。

第四条这里有概念就是包含块(containing block), 包含块其实是一个视觉上的概念,可以理解成一个矩形框,有点类似于盒模型。包含块为里面包含的元素提供一个参考,元素的尺寸和位置往往是这个包含块决定的。其实说起来还是有点懵对吧?我们后面在具体研究一下这个,算是留个悬念。这个概念的内容量有点多,理解了包含块也就理解了第四条所说的了。

接下来我们主要来看一下第三条边距折叠,还是先来撸一段代码

<div style="background-color:blue;margin-bottom: 10px;height:20px"></div>
<div style="background-color:blue;margin-top: 20px;height:20px;"></div>

浏览器渲染效果如下

我们会发现两个元素之间的间距跟元素高度一样,只有20px,而是我们设置的30px。这个现象叫边距折叠。从上面的栗子我们可以得出一个结论,两个元素之间的间距取决于两者中margin更大的那一个元素。

接着上面的栗子我们在延伸一下

<div style="height:20px;background-color:red;"></div>
<div style="background-color:pink;">
  <div style="background-color:blue;margin: 10px 10px;height:20px"></div>
	<div style="background-color:blue;margin: 20px 10px;height:20px;"></div>
</div>

浏览器渲染效果如下

有没有发现一个问题?我们第一个元素的上外边距和第二个元素的下外边距并没有把我们的父元素撑开, 而且这个间距作用到了父元素外面。那如果我只想这个外边距作用在父元素里呢?有什么办法吗?当然有,我们只需要在父元素加一个css属性就是overflow:hidden, 代码如下

<div style="height:20px;background-color:red;"></div>
<div style="background-color:pink;overflow:hidden;">
  <div style="background-color:blue;margin: 10px 10px;height:20px"></div>
	<div style="background-color:blue;margin: 20px 10px;height:20px;"></div>
</div>

浏览器渲染效果如下

从渲染效果来看,我们发现margin已经作用在父元素内了。其实这是因为我们创建了一个新的BFC,变成了一个独立的渲染区域。

还有一种情况就是包含了浮动的元素, 还是先看代码

<div style="float: left;width: 100px;height: 100px;background-color: blanchedalmond;margin: 20px;"></div>
<div style="background-color: blueviolet;overflow: hidden;margin: 30px;">
  1990年,Tim Berners-Lee和Robert Cailliau共同发明了Web。1994年,Web真正走出实验室。 [3] 
  从HTML被发明开始,样式就以各种形式存在。不同的浏览器结合它们各自的样式语言为用户提供页面效果的控制。最初的HTML只包含很少的显示属性。
</div>

我们看一下浏览器渲染效果

如果去掉了文字所在元素的overflow:hidden会出现什么样的效果呢?其实就是正常的文字环绕效果,这个你可以自己验证一下。

总结

理论结合着实践,其实你会发现理解起来没这么难。只看理论有时候就是雾里看花,给人一种很朦胧的感觉。BFC作为一个在面试中常见的问题,需要我们加深理解,这个是在工作中也会遇到和用到的知识。

希望这篇文章能帮助到你。由于水平有限,如果文章上有什么问题,希望大家留言指正。