【前端帮帮忙】第8期 关于BFC,你需要了解的

836 阅读11分钟

BFC是耳熟能详的一个东西了,经常听到,其实在项目中也经常用到,比如最常用的清除浮动,自适应两栏布局等等。只是都没有去深究其原理和相关的知识点,今天就一起来好好学习一下吧。

要明白BFC是什么,我们要先来了解几个相关的概念。

盒模型

MDN对其描述如下:

当对一个文档进行布局(lay out)的时候,浏览器的渲染引擎会根据标准之一的CSS基础盒模型(CSS basic box model),将所有元素表示为一个个矩形的盒子(box)。CSS决定这些盒子的大小、位置和以及属性(例如颜色、背景、边框尺寸...)。

每个盒子由四个部分(或称区域)组成,其效用由它们各自的边界(Edge)所定义(原文:defined by their respective edges,可能意指容纳、包含、限制等)。如图,与盒子的四个组成区域相对应,每个盒子有四个边界:内容边界 Content edge、内边距边界 Padding Edge、边框边界 Border Edge、外边框边界 Margin Edge。

我们在浏览器的控制台也可以很清楚的看到页面的每一个元素(除了单独的文本元素),其实都是一个盒子:

更加详细的介绍可以参考MDN或者W3C规范的描述:

www.ayqy.net/doc/css2-1/…

视觉格式化模型

MDN 对其描述如下:

CSS视觉格式化模型(visual formatting model)是用来处理和在视觉媒体上显示文档时使用的计算规则。该模型是CSS的基础概念之一。

视觉格式化模型会根据CSS盒子模型将文档中的元素转换为一个个盒子,每个盒子的布局由以下因素决定:

  • 盒子的尺寸:精确指定、由约束条件指定或没有指定
  • 盒子的类型:行内盒子(inline)、行内级盒子(inine level)、原子行内级盒子(atomic inine-level)、块盒子(block)
  • 定位方案(position scheme):普通流定位、浮动定位或绝对定位
  • 文档树中的其他元素:即当前盒子的子元素或兄弟元素
  • 视口尺寸与位置
  • 所包含的图片的尺寸
  • 其他的某些外部因素

关于不同类型盒子的介绍,大家可以直接看MDN,写的很详细了,这里就不再阐述。这边着重讲下定位方案

定位方案

一旦生成了盒子,CSS引擎就需要定位它们以完成布局。在定位的时候,浏览器会根据元素的盒类型和上下文对这些元素进行定位,可以说盒就是定位的基本单位。定位时有三种定位方案,分别是:常规流(即普通流)、浮动流以及绝对定位。

常规流(Normal flow)

  • 在常规流中,盒子一个接着一个排列;
  • 块级格式化上下文里面,它们竖着排列;
  • 行内格式化上下文里面,它们横着排列;
  • positionstaticrelative,并且floatnone时,会触发常规流
  • 对于静态定位(static positioning),position: static,盒的位置是常规流布局里的位置;
  • 对于相对定位*(relative positioning),position: relative,盒偏移位置右这些属性定义:topbottomleftright即使有偏移,仍然保留原有的位置,其他常规流不能占用这个位置。

浮动(Floats)

  • 盒成为浮动盒(floating boxes);
  • 它定位于当前行的开头或末尾;
  • 导致常规流环绕在它的周边,除非设置clear属性;

绝对定位(Absolute positioning)

  • 绝对定位方案,盒从常规流中被移除,不影响常规流的布局;
  • 它的定位相对于它的包含块,相关CSS属性:topbottomleftright
  • 对于position: absolute,元素定位相对于最近的一个relativeabsolutefixed的父元素,如果没有则相对于body

FC(Formatting context)

Formatting context是W3C CSS2.1规范中的一个概念。它是页面中一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。最常见的Formatting context有Block formatting context(简称BFC)和Inline formatting context(简称IFC)。

CSS2.1中只有BFCIFC,CSS3中还增加了GFCFFC。我们主要来讲下BFC

好了,接下来轮到我们的主角BFC出场了。

BFC是什么

BFC(Block Formatting Context)直译为块级格式化上下文。它是一个独立的渲染区域,只有Box-level box参与,它规定了内部的Block-level box如何布局,并且与这个区域外部毫不相干。

讲了这么多概念,说下自己的理解,如果有不对的地方,烦请指出,感激不尽。

  • 当我们对一个页面进行布局的时候,浏览器的渲染引擎会根据CSS的盒模型将所有元素表示为一个个盒子;
  • 盒子的定义是由视觉格式化模型来定义的,盒子的类型可以分为:行内盒子(inline)、行内级盒子(inine level)、原子行内级盒子(atomic inine-level)、块盒子(block);
  • 接下来就是真正的布局开始,浏览器会根据盒子的类型和所处的上下文来对这些元素进行定位,定位有3种方案,分别是:普通流、浮动流和绝对定位。
  • 普通流中的盒子就是属于一个格式化上下文,可能是块或是行内(格式化上下文),但不能两者都是。块级盒参与块格式化上下文。行内级盒参与行内格式化上下文。

是不是可以这样理解:BFC就是普通流中的元素布局定位时的一个执行环境?

BFC布局规则

  • 内部的Box会在垂直方向,一个接一个的放置。(这边不是很理解,内部的Box不是也可能有行内元素吗?行内元素不是按水平方向来排列吗?这边可能还需要参考行内格式化上下文来理解,还有一个是writing-mode属性。)
  • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
  • 每个元素的margin box的左边,与包含快border box的左边相接触(对于从左往右的格式化,否则相反),即使存在浮动也是如此(除非该盒建立了一个新的块格式化上下文)
  • BFC的区域不会与float box重叠
  • BFC就是页面上的一个隔离的独立的容器,容器里面的子元素不会影响到外面的元素,反之亦如此
  • 计算BFC的高度时,浮动元素也参与计算

如何创建BFC

  • 根元素
  • float属性值不为none
  • positionabsolutefixed
  • displayinline-blocktable-celltable-captionflexinline-flex
  • overflow不为visible
  • ...

其实还有很多,这里列出的是一些比较常用的,跟详细的可以看MDN

通过几个实例来加深理解

1.自适应两栏布局

实现自适应两栏布局的方法有很多,但是我觉得BFC的方式应该是最简单的了。

<body>
  <div class="container">
    <div class="side"></div>
    <div class="main"></div>
  </div>
</body>
.container {
  width: 400px;    
}

.side {
  float: left;
  width: 100px;
  height: 100px;
  background: lightpink;
}

.main {
  height: 300px;
  background: lightblue;
}

页面截图:

根据BFC布局规则第3条:

每个元素的margin box的左边,与包含快border box的左边相接触(对于从左往右的格式化,否则相反),即使存在浮动也是如此(除非该盒建立了一个新的块格式化上下文)

即使存在浮动元素sidemain的左边依然会与包含快的左边相接触。

根据BFC布局规则第4条:

BFC的区域不会与float box重叠

所以,我们可以给main创建一个新的BFC,这样就不会跟浮动的side重叠了,它会根据包含块的宽度和side的宽度,自动变窄。

main加上overflow: hidden

main {
  overflow: hidden;
}

再来看下效果:

2.清除内部浮动

<div class="parent">
  <div class="child"></div>
</div>
.parent {
   width: 200px;
   border: 2px solid blue;
   background: lightblue;
}

.child {
  float: left;
  width: 100px;
  height: 100px;
  border: 2px solid red;
  background: lightcoral;
}

页面截图:

可以看到,由于子元素设置了浮动,而父元素又没有设置高度,导致父元素高度塌陷了:没有自动被子元素的高度撑开。

根据BFC布局规则第6条:

计算BFC的高度时,浮动元素也参与计算

我们可以给父元素parent触发BFC,那么它在计算高度时,内部的浮动元素也会参与计算。

.parent {
  overflow: hidden;
}

再看下效果:

3.阻止margin重叠

<div class="box">box</div>
<div class="box">box</div>
.box {
  width: 200px;
  height: 200px;
  margin: 100px;
  background: red;
}

页面截图:

根据BFC布局规则第2条:

Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠

可以看到,第一个div的下边距跟第二个的上边距发生了重叠。

重叠的结果按照如下规则计算:

  • 两个相邻的外边距都是正数时,重叠结果是它们两者之间较大的值。
  • 两个相邻的外边距都是负数时,重叠结果是两者绝对值的较大值。
  • 两个外边距一正一负时,重叠结果是两者的相加的和。

产生折叠的必备条件:margin必须是邻接的!

我们只要给其中一个div外层再包裹一层div,然后触发其生成一个新的BFC,它们就不会发生重叠了。

<div class="box">box</div>
<div class="new-bfc">
  <div class="box">box</div>
</div>
.new-bfc {
  overflow: hidden;
}

页面截图:

还有很多其他的例子,比如可以避免文字环绕、多列布局等等,这里就不再一一列举,大家有兴趣的可以自己多尝试下,这里有一个网址可以在线演示,更加直观, 链接地址:www.cnblogs.com/xiaohuochai…

创建BFC的新方式

我们上面举的例子都是通过overflow来创建BFC,但是其实这个方法会有两个问题。

  1. 这些方法本身是有自身的设计目的的,所以在使用它们创建BFC时可能会存在一些副作用。例如,使用overflow创建BFC后,在某些情况下可能会看到出现一个滚动条或者元素内容被裁切。这是由于overflow属性的设计是用来让你告诉浏览器如何定义元素的溢出状态的。浏览器执行了它最基本的定义。
  2. 另一个问题是,即使在没有出现副作用的情况下,使用overflow也可能会使另一个开发人员感到困惑。他们可能会各种猜想:这里为什么要把overflow的值设为autohidden?原来的开发人员这样做的意义是什么?原来的开发人员是想让这里出现滚动条吗?

所以实际项目开发中,还需要根据项目的需求来选择合适的方法,最好也能在代码里写明注释。

那有没有什么更好的方式呢?CSS工作组定义了一个新的属性值:display: flow-root

你可以使用display: flow-root安全的创建BFC,来解决上文中提到的各种问题:自适应两栏布局、清除内部浮动、阻止margin重叠等等。

caniuse上display: flow-root在各浏览器的兼容情况,看图:

目前来看,兼容性还是差了一点。

有关于flow-root的详细介绍可以看这篇文章:www.w3cplus.com/css3/displa…

BFC概念理解

可以想象一下,BFC就相当于我们现实中的一个纸箱(盒子),箱子里面的东西的放置(布局)是不会受到外部其他东西的影响的,它形成了一个独立的封闭的区域。当然它里面东西的放置(布局)也不会影响到外面的东西。

最后

感谢您的阅读,希望对你有所帮助,希望你能通过这篇文章能对BFC有一个比较全面的理解并能实际应用到项目开发中。本人水平有限,如果文中有不当的地方烦请指正,感激不尽。

关注

欢迎大家关注我的公众号前端帮帮忙,一起交流学习,共同进步。

参考:

视觉格式化模型

块格式化上下文

前端精选文摘:BFC 神奇背后的原理

学习 BFC (Block Formatting Context)

[布局概念] 关于CSS-BFC深入理解

理解CSS布局和BFC

什么是BFC