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

盒模型

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';
})

图示如下:

image

视觉格式化模型(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):

image

盒子的生成

盒子的生成是 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)

定义

浮动、绝对定位元素、块容器(例如inline-blocks、table-cells、and table-captions)以及溢出而非可视的元素(除非该值已经传播到了视口)都是建立 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而言,如果是别的书写顺序,则按照该顺序来计算。

如果宽度不是 auto 或者 'border-left-width'+'padding-left'+'width'+'padding-right'+'border-right-width' 的结果大于包含块的宽度,对于以下规则,被视为零。

如果只有一个值指定为'auto',则其使用的值来自相等。

如果宽度设置为 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
也可以扫码添加好友,备注“SF”就行
https://fish-pond-1253945200.cos.ap-guangzhou.myqcloud.com/img/base/wx-qrcode1.jpg


鱼头的Web海洋
鱼头的Web海洋

Kris不只是一只鱼头

959 声望
3.1k 粉丝
0 条评论
推荐阅读
用神奇的 form 验证 API 来优化你的表单验证
鱼头曾在 『极限版』不掺水,用纯 CSS 来实现超飒的表单验证功能 一文中分享过一个花里胡哨的 纯 CSS 的表单验证功能 。虽然仅仅依赖 CSS 是无法满足我们的日常开发需求的,但是配合着各种原生的 form 验证 API ...

陈大鱼头5阅读 2.2k

还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库

XboxYan34阅读 2.2k评论 2

封面图
CSS 如何设置自动滚动定位的“安全”间距?
欢迎关注我的公众号:前端侦探介绍两个和滚动定位相关的 CSS 属性:scroll-padding和 scroll-margin在平时开发中,经常会碰到需要快速定位的问题,比如常见的锚点定位 {代码...} 这样,在点击a标签时会自动定位到...

XboxYan30阅读 2.2k评论 2

封面图
CSS transition 小技巧!如何保留 hover 的状态?
欢迎关注我的公众号:前端侦探通常情况下,hover 是无法保存状态的。鼠标移入触发额外样式,一旦移出就还原了 {代码...} 这就意味着,如果需要保留hover的状态,可能就不得不借助JS了,比如下面是某某书院的首页...

XboxYan29阅读 3.5k评论 2

封面图
现代 CSS 之高阶图片渐隐消失术
在过往,我们想要实现一个图片的渐隐消失。最常见的莫过于整体透明度的变化,像是这样: {代码...} {代码...} 但是,CSS 的功能如此强大的今天。我们可以利用 CSS 实现的渐隐效果已经不再是如此的简单。想想看,...

chokcoco25阅读 2k

封面图
除了 filter 还有什么置灰网站的方式?
大家都知道,当一些重大事件发生的时候,我们的网站,可能需要置灰,像是这样:当然,通常而言,全站置灰是非常简单的事情,大部分前端同学都知道,仅仅需要使用一行 CSS,就能实现全站置灰的方式。像是这样,我...

chokcoco19阅读 1.6k评论 1

封面图
vue中style scope深度访问新方式(:deep())
1、&gt;&gt;&gt;如果vue的style使用的是css,那么则 {代码...} 但是像scss等预处理器却无法解析&gt;&gt;&gt;,所以我们使用下面的方式.2、/deep/ {代码...} 但是有些开发者反应,在vue-cli3编译时,deep的方式会...

寒水寺一禅11阅读 34.8k评论 9

Kris不只是一只鱼头

959 声望
3.1k 粉丝
宣传栏