CSS

块格式化上下文(Block Formatting Context,BFC) 前世今生

Posted on 2019-08-20,10 min read

在看了网上类似于BFC的技术博客后,也是看了好几篇再加上看了官方文档后,才大概对BFC这个面试经常问的东西有了一点理解,所以就抱着尽量通俗易懂的方式给后面需要了解BFC特性同学讲解,尽量让你只需看这一篇就能有个大概的理解,这也是本次写这篇的文章的初衷。

前世

BFC是什么?为什么有这个东西?它有什么作用?为了回答这些问题,我们先来想一想这么一个场景:盘古开天地之际,一个页面上面只有几个div,span元素,这个时候没有块级元素和行内级元素的概念,更没有边框边距的概念,那请问这几个元素该怎么排列表现呢?

为了解决这个问题,女娲(w3c),先教给这几个元素一些生存的本领吧,不然饿死了咋办,于是它们学会了穿衣服(padding),防护罩(margin)等,这也就是盒模型的由来,当然,盒模型不是本次的重点,我们只需要知道盘古开天辟地之际div,span等元素都学会了盒模型这一本领。

本领都学会了,那不得派它们出去帮女娲(w3c)干活,但是大家学的本领都一样,而且每个地方的环境都是千差万变的,这可不行,于是女娲(w3c)便开始对不同类型的元素传授不同技能,时间久了,女娲(w3c)觉得这样子太耗精力,便成立了一个部门(视觉格式化模型),专门将这些学会本领的元素二次培训成更高级的元素,简称盒子,而它们唯一的身份区别就是display属性,这些盒子在不同的地方任职(指盒子的布局),其权力(盒子的布局因素)受限于四大因素影响:

  • 盒子尺寸和类型
  • 定位方案(普通流定位、浮动定位或绝对定位)
  • 文档树中元素之间的关系
  • 外部因素(例如,视口大小,图像的固有尺寸等)

随着历史发展,这些盒子通过自身的修炼已经发展成不同的类型了,也衍生出了很多新的概念,具体如下:

概念 描述
文档流中的独立的区域,相互之间在垂直方向上按照顺序依次堆叠
包含块 包含其他盒子的块称为包含块
盒子 CSS引擎根据文档中的内容所创建,主要用于文档元素的定位、布局和格式化等(多个元素可以合并成一个盒子,一个元素也可生成多个盒子,如匿名盒子)
块级元素 displayblocklist-itemtable时,该元素将成为块级元素(元素是否是块级元素仅是元素本身的属性,并不直接用于格式化上下文的创建或布局)
块级盒子 由块级元素生成(一个块级元素至少会生成一个块级盒子,但也有可能生成多个,例如列表项元素)
块容器盒子 块容器盒子侧重于当前盒子作为“容器”的这一角色,它不参与当前块的布局和定位,它所描述的仅仅是当前盒子与其后代之间的关系。换句话说,块容器盒子主要用于确定其子元素的定位、布局等
块盒子 块级盒子+块容器盒子简称块盒子(还可以分为匿名块盒子)
行内级元素 display 为 inlineinline-blockinline-table 的元素称为行内级元素(同块级元素类似,行内级元素仅是元素本身的属性,不直接用于格式化上下文的创建或布局)
行内级盒子 由行内级元素生成
行内盒子 参与行内格式化上下文创建的行内级盒子称为行内盒子(也分匿名行内盒子)
原子行内级盒子 不参与行内格式化上下文创建的行内级盒子称为原子行内级盒子

最后整理了一个导航图,方便大家快速记忆:

篇幅有限,如果想具体的了解这些盒子的介绍以及说明,请点击这里

今生

what

通过上面的故事回忆并结合,我们再来讲讲块格式化上下文(Block Formatting Context,BFC),大体我们可以认为BFC是Web页面的可视化CSS渲染的一部分内容,如果再具体一点就是:

  • 块盒子的布局过程发生的区域
  • 浮动元素与其他元素交互的区域

块盒子在上面我们已经讲了,就是块级盒子+块容器盒子简称块盒子;而浮动元素这里主要指元素的 float 不是 none的元素。

why

通过前世我们可以大致发现BFC主要是因为前端发展越来越快,页面布局日益复杂,为了应对这些需求从而应运而生,并且其拥有一些特殊的特性,我们从而可以利用这些特性去解决某一类问题,如清除浮动,防止margin重叠等问题。

how

在我们谈BFC有什么作用之前,我们得知道怎么触发BFC机制,这是先决条件,通过查阅文档并整理了表格如下:

条件太多,可能一时半会儿也记不住,可以添加为书签方便日后查找,现在我们来探讨一下BFC都有哪些特性,通过查阅规范文档可以总结如下:

  • 块级盒从一个包含块的顶部开始一个接一个地垂直排列
  • 两个兄弟块级盒之间的垂直距离由margin属性决定,并且在同一个BFC中的元素发生margin collapse
  • 每个块级盒的左外边缘相邻包含块的左边缘(同理从左往右格式化,则与包含块的右边缘相邻)
  • BFC具有‘密室’特性,密室里面的元素不会影响到密室外面的元素,反之亦然
  • 计算BFC的大小时,考虑所有所包含的元素,包括浮动元素

文字总是给人苍白无力的感觉,还是让我们看看demo是怎么一一解释以上特性的。

示例一

根元素(这里指HTML)默认会创建一个BFC,我们可以看到块级盒是从上到下垂直排列,也就是我们的特性一(块级盒从一个包含块的顶部开始一个接一个地垂直排列),块级盒就是这里的h1,pdiv标签所渲染的盒子,而包含块就是我们这里的HTML标签默认创建的BFC区域,最终的渲染结果就是从包含块的顶部一个接着一个的垂直排列,从而很好的解释了BFC得特性一。

示例二

由示例二我们可以看出,在同一个BFC中,块级盒垂直方向的距离由上下 margin 属性决定,并且会发生margin callapse,其规则可以简记同大异和。翻译过来就是如果两个元素margin都为正值,则取两者之中较大的那一个为,如果两个元素margin都为负值,则取两者之中绝对值较大的那一个,这就是相同符号的取较大的那一个,简记同大;如果两个元素margin为一正一负,则取两个margin相加之和为最终的值,简记异和,具体效果我们可以查看示例三。

示例三

通过示例三,我们也了解了margin collapse规则,这也验证了BFC的特性二(两个兄弟块级盒之间的垂直距离由margin属性决定,并且在同一个BFC中的元素发生margin collapse),下面我们来看看BFC的特性三**的例子。

示例四

通过打开浏览器的审查工具,将鼠标移到h1标签的margin属性上面,即可看到margin属性所渲染的区域:

同理我们将鼠标移到div标签的padding属性上面,即可看到padding所渲染的区域:

通过结合两张图我们可以清晰看到每个块级盒的左外边缘(margin)相邻包含块的左边缘(padding),从而验证了BFC的特性三(每个块级盒的左外边缘相邻包含块的左边缘)

示例五

默认情况下我们可以看到区域一直接跑到了左浮动区域底部,如果点击设置BFC按钮,我们可以看到区域一跑到了右上角,因为BFC具有密室特性,在密室内的元素都不会跑到密室外面从而造成溢出,但是只要我们点击取消BFC,我们可以马上看到区域一一下就溢出了,这也很好的解释了BFC得特性四(BFC具有‘密室’特性,密室里面的元素不会影响到密室外面的元素)

示例六

默认情况下父级标签触发了BFC左浮动区域设置了float:left,似乎一切都符合我们的认知预期,但是当我们点击取消BFC按钮时,却发现左浮动区域的背景色没了,打开审查工具查看发现父级元素好像真的并没有包裹左浮动区域左浮动区域似乎‘溢出’了父级标签的容器,我们都知道浮动元素特性就是该元素从网页的正常流动(文档流)中移除,尽管仍然保持了部分的流动性,原来取消掉BFC才是我们常见的情景,也就是浮动元素会造成高度坍塌,解决这种问题常用解决方案有很多,比如利用伪元素清除浮动,在这里我们利用BFC特性5(计算BFC的大小时,考虑所有所包含的元素,包括浮动元素),我们只需要让父级元素触发BFC即可,这里我们使用overflow进行触发,具体效果我们可以点击设置BFC按钮进行预览。

作用

在前面我们讲述了BFC得前世今生,对BFC的触发条件、特性都进行了一一介绍,BFC我们似乎是懂了不少,但是BFC具体有哪些实际作用我们还是需要学习的,正所谓学以致用,下面我们看看有哪些:

示例七

结尾

BFC的概念这里也是结合笔者自身理解以及查阅了一些文档总结而成,如文中有论述错误的地方,还望批评指正。

参考
[1]Understanding Block Formatting Contexts in CSS
[2]block-formatting
[3]格式化上下文
[4]视觉格式化模型

作者:落叶卢生
链接:https://luoyelusheng.com/post/块格式化上下文(Block Formatting Context,BFC) 前世今生
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

下一篇: 深度解析之异步加载(defer、async、module)和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)→