前端进阶之什么是BFC?BFC的原理是什么?如何创建BFC?

24,564 阅读7分钟

盒模型

The CSS box model describes the rectangular boxes that are generated for elements in the document tree and laid out according to the visual formatting model.

CSS盒模型描述了通过 文档树中的元素 以及相应的 视觉格式化模型(visual formatting model) 所生成的矩形盒子。

基础盒模型(CSS basic box model)

当浏览器对一个 render tree 进行渲染时,浏览器的渲染引擎就会根据 基础盒模型(CSS basic box model) ,将所有元素划分为一个个矩形的盒子,这些盒子的外观,属性由CSS来决定。

我们在浏览器控制台输入如下代码就可以看到页面的每一个元素都是由一个矩形来包裹的,这些就是盒子

?('*').forEach(e => {
  e.style.border = '1px solid';
})

图示如下:

视觉格式化模型(visual formatting model)

CSS视觉格式化模型(visual formatting model) 是根据 基础盒模型(CSS basic box model)文档(doucment) 中的元素转换一个个盒子的实际算法。

官方说法就是: 它规定了用户端在媒介中如何处理文档树( document tree )。

每个盒子的布局由以下因素决定:

  • 盒子的尺寸
  • 盒子的类型:行内盒子 (inline)行内级盒子 (inline-level)原子行内级盒子 (atomic inline-level)块级盒子 (block-level)
  • 定位:正常流浮动绝对定位
  • 文档树中当前盒子的子元素兄弟元素
  • 视口(viewport)尺寸位置
  • 盒子内部图片的尺寸
  • 其他某些外部因素

视觉格式化模型(visual formatting model) 的计算,都取决于一个矩形的边界,这个矩形,被称作是 包含块( containing block ) 。 一般来说,(元素)生成的框会扮演它子孙元素包含块的角色;我们称之为:一个(元素的)框为它的子孙节点建造了包含块。包含块是一个相对的概念。

例子如下:

<div>
    <table>
        <tr>
            <td>hi</td>
        </tr>
    </table>
</div>

以上代码为例,divtable 都是包含块。divtable 的包含块,同时 table 又是 td 的包含块,不是绝对的。

图示:(图片来自w3help):

盒子的生成

盒子的生成是 CSS视觉格式化模型 的一部分,用于从文档元素生成盒子。盒子的类型取决于CSS display 属性。

格式化上下文(formatting context) 是定义 盒子环境 的规则,不同 格式化上下文(formatting context) 下的盒子有不同的表现。

以下是盒子相关的概念定义:

  • 块级元素

    • 当元素的displayblocklist-itemtable 时,它就是块级元素。
  • 块级盒子

    • 块级盒子用于描述它与父、兄弟元素之间的关系。
    • 每个块级盒子都会参与 块格式化上下文(block formatting context) 的创建。
    • 每个块级元素都会至少生成一个块级盒子,即主块级盒子(principal block-level box)
    • 主块级盒子包含由后代元素生成的盒子以及内容,同时它也会参与定位方案。
    • 一个同时是块容器盒子的块级盒子称为块盒子(block box)
  • 匿名盒子

    • 某些情况下需要进行视觉格式化时,需要添加一些增补性的盒子,这些盒子不能被CSS 选择器选中,也就是所有可继承的 CSS 属性值都为 inherit ,而所有不可继承的 CSS 属性值都为 initial。因此称为匿名盒子(anonymous boxes)
  • 行内元素

    • 当元素的displayinlineinline-blockinline-table 时,它就是行内级元素。
    • 显示时可以与其他行内级内容一起显示为多行。
  • 行内盒子

    • 行内级元素会生成行内级盒子,该盒子同时会参与行内格式化上下文(inline formatting context)的创建。
  • 匿名行内盒子

    • 类似于块盒子,CSS引擎有时候也会自动创建一些行内盒子。这些行内盒子无法被选择符选中,因此是匿名的,它们从父元素那里继承那些可继承的属性,其他属性保持默认值 initial
  • 行盒子

    • 行盒子由行内格式化上下文创建,用来显示一行文本。在块盒子内部,行盒子总是从块盒子的一边延伸到另一边(译注:即占据整个块盒子的宽度)。当有浮动元素时,行盒子会从向左浮动的元素的右边缘延伸到向右浮动的元素的左边缘。
  • run-in 盒子(在CSS 2.1的标准中移除了)

    • run-in盒子可以通过display: run-in来设置,它既可以是块盒子,又可以是行内盒子,这取决于它后面的盒子的类型。

BFC(Block formatting contexts)

BFC 这个概念来自于 视觉格式化模型(visual formatting model) 中的 正常流(Normal flow)

定义

浮动元素、绝对定位元素,'display' 特性为 "inline-block","table-cell", "table-caption" 的元素,以及 'overflow' 不是 "visible" 的元素,会创建新的 BFC(Block formatting contexts)

注意,是这些元素创建了块格式化上下文,它们本身不是块格式化上下文。

表现

BFC(Block formatting contexts)中,在包含块内一个盒子一个盒子不重叠地垂直排列,两个兄弟盒子直接的垂直距离由margin决定。浮动也是如此(虽然有可能两个盒子的距离会因为floats而变小),除非该盒子再创建一个新的BFC

鱼头注:简单来说,BFC就是一个独立不干扰外界也不受外界干扰的盒子啊(/ω\)。

块级相关的计算

正常流中的块级与非替换元素

'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = 包含块的宽度

上面的计算法则是基于 writing-mode: ltr 而言,如果是别的书写顺序,则按照该顺序来计算。

如果'width'不是 auto 并且'border-left-width'+'padding-left'+'width'+'padding-right'+'border-right-width'(加上'margin-left'或不是'auto'的'margin-right')大于包含块的宽度,则对于以下规则,'margin-left'或'margin-right'的任何'auto'值都将被视为零。

如果将'width'设置为 auto ,则其他所有 auto 值将变为 0

如果宽度设置为 auto ,则任何其他 auto 值都会变为 0 ,并且宽度会跟着所以盒子的情况铺满。

如果 'margin-left''margin-right' 都为 auto ,则会使元素相对于包含块的边缘水平居中。

浮动与非替换元素

如果 'margin-left''margin-right' 都为 auto ,则它们的具体值为 0

如果宽度为 auto ,则使用 shrink-to-fit 的宽度计算方式(CSS 2.2没有定义精确的算法)。

然后 shrink-to-fit 大概的计算方式则是:min(max(preferred minimum width, available width), preferred width)

绝对定位与非替换元素

'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right' = 包含块的宽度

如果 'left''width''right' 都是 'auto',则首先将 'margin-left''margin-right''auto' 值设置为 0

如果 'left''width''right' 都不是 'auto',则按照实际值来算。

如果 'margin-left''margin-right' 都为 0 ,则根据 'left''width''right' 的值是否是 'auto' 来计算。 如果 一个方向值'width' 的值是 'auto',而 '另一个一个方向值' 不是,则宽度使用 shrink-to-fit 算法计算。如果一个值为 'auto' 而另外两个值不算,则该值使用 shrink-to-fit 来计算。

上面的计算法则是基于 writing-mode: ltr 而言,如果是别的书写顺序,则按照该顺序来计算。

鱼头注:这里特别说明一点,在MDN中依然把flexbox跟gridbox 算在 BFC中,但在最新的规范里,它们已经从BFC中分离了出去,成为独立的一个CSS模块,内容如下:

  1. CSS Flexible Box Layout Module Level 1
  2. CSS Grid Layout Module Level 2

如果你喜欢探讨技术,或者对本文有任何的意见或建议,非常欢迎加鱼头微信好友一起探讨,当然,鱼头也非常希望能跟你一起聊生活,聊爱好,谈天说地。 鱼头的微信号是:krisChans95