BFC详解

91 阅读6分钟

前言:BFC是css里面比较重要又相对不太容易理解的概念;BFC也牵扯了很多其它的CSS知识,例如:浮动、定位、盒模型等,下面我们就一起深入了解学习一下所谓的BFC

1.什么是BFC

1.1 定义:

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

说人话就是:BFC是一个独立的布局环境,BFC内部的元素布局与外部互不影响

1.2 Box、Formatting Context

在解释什么是BFC之前,我们需要先知道Box、Formatting Context的概念。

1.2.1 Box:css布局的基本单位,也就是我们常说的 盒子模型

盒子模型:它包括:边距[margging],边框[border],填充[padding],和实际内容[width、height]

image.png

Box是css布局的基本单位,也就是说,我们的页面就是由N个Box组成的。元素的类型和 display 属性,决定了这个 Box 的类型。 不同类型的 Box, 会参与不同的 Formatting Context(一个决定如何渲染文档的容器),因此Box内的元素会以不同的方式渲染。让我们看看有哪些盒子:

  • block-level box (块状盒子模型)

    块状盒子模型: display 属性为 block, list-item, table 的元素,会生成 block-level box。并且参与 block fomatting context;

  • inline-level box (内联盒子模型)

    行内盒子模型:display 属性为 inline, inline-block, inline-table 的元素,会生成 inline-level box。并且参与 inline formatting context;

  • run-in box (被修饰盒子模型 css3)

    被修饰盒子模型:display属性为:run-in,被修饰的元素根据上下文决定是块元素还内联元素

    (1)如果 run-in box 包含 block box,那么这个 run-in box 也成为 block box

    (2)如果紧跟在 run-in box 之后的兄弟节点是 block box,那么这个 run-in box 就会做为此 block box 里的 inline box,run-in box 不能进入已经一个已经以 run-in box 开头的块内,也不能进入本身就是 display:run-in; 的块内

    (3)否则,run-in box 成为 block box

1.2.2 Formatting context

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

1.3 BFC布局规则

  1. 内部的Box会在垂直方向,一个接一个地放置。
  2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
  3. 每个元素的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
  4. BFC的区域不会与float box重叠。
  5. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
  6. 计算BFC的高度时,浮动元素也参与计算

1.4 如何创建BFC

  1. 根元素
  2. float的值不是none
  3. position的值不是static或者relative。
  4. display的值是inline-block、table-cell、flex、table-caption或者inline-flex
  5. overflow的值不是visible

2. BFC有什么作用

2.1利用BFC避免margin重叠。

先上代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>bfc防止margin重叠</title>
</head>
<style>
    *{
        margin: 0;
        padding: 0;
    }
    p {
        color: #f55;
        background: green;
        width: 200px;
        line-height: 100px;
        text-align:center;
        margin: 30px;
    }
</style>
<body>
    <p>我是p1</p>
    <p>我是p2</p>
</body>
</html>

image.png

body元素是BFC,根据BFC布局规则,第二条同一个BFC内部的两个相邻的Box的margin会重叠,现在我们不想让它们的margin重叠,只需要把它们转化为BFC就可以了。使用div将其中一个

标签包裹,激活BFC

    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>bfc防止margin重叠</title>
</head>
<style>
    *{
        margin: 0;
        padding: 0;
    }
    p {
        color: #f55;
        background: green;
        width: 200px;
        line-height: 100px;
        text-align:center;
        margin: 30px;
    }
    div{
        overflow: hidden;
    }
</style>
<body>
    <p>我是p1</p>
    <div>
        <p>我是p2</p>
    </div>
</body>
</html>

再看效果: image.png

2.2自适应两栏布局

根据BFC布局规则第三条,每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。 我们创建两个盒子,其中一个浮动,效果如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>自适应两端布局</title>
</head>
<style>
    *{
        margin: 0;
        padding: 0;
    }
    body {
        width: 100%;
        position: relative;
    }
 
    .left {
        width: 100px;
        height: 150px;
        float: left;
        background: rgb(139, 214, 78);
        text-align: center;
        line-height: 150px;
        font-size: 20px;
    }
 
    .right {
        height: 300px;
        background: rgb(170, 54, 236);
        text-align: center;
        line-height: 300px;
        font-size: 40px;
    }
</style>
<body>
    <div class="left">LEFT</div>
    <div class="right">RIGHT</div>
</body>
</html>

image.png

很明显,浮动的left盒子一直处于right上层。我们再根据BFC布局规则第四条:BFC的区域不会与float box重叠,将right盒子激活BFC,right会自动的适应宽度,这时候就形成了一个两栏自适应的布局。

上面的代码,给right盒子加上 overflow: hidden;就可以了。

image.png

2.3 清除浮动

众所周知,当我们子元素设置浮动,父元素有没有高度,就会发生高度塌陷,所以要清除浮动。

如下的塌陷代码:

    <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>清除浮动</title>
</head>
<style>
    .father {
        border: 5px solid green;
        width: 300px;
    }
    
    .child {
        border: 5px solid red;
        width:100px;
        height: 100px;
        float: left;
    }
</style>
<body>
    <div class="father">
        <div class="child"></div>
        <div class="child"></div>
    </div>
</body>
</html>

image.png

这个时候,根据BFC规则最后一条计算BFC的高度时,浮动元素也参与计算,将父元素激活BFC:

给父元素添加:overflow: hidden;

image.png

3.总结

因为BFC内部的元素和外部的元素绝对不会互相影响,因此, 当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠。同样的,当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度。避免margin重叠也是这样的一个道理。

4.扩展 :GFC FFC

4.1 GFC

GFC(GridLayout Formatting Contexts)直译为"网格布局格式化上下文",当为一个元素设置display值为grid的时候,此元素将会获得一个独立的渲染区域,我们可以通过在网格容器(grid container)上定义网格定义行(grid definition rows)和网格定义列(grid definition columns)属性各在网格项目(grid item)上定义网格行(grid row)和网格列(grid columns)为每一个网格项目(grid item)定义位置和空间。

那么GFC有什么用呢,和table又有什么区别呢?
首先同样是一个二维的表格,但GridLayout会有更加丰富的属性来控制行列,控制对齐以及更为精细的渲染语义和控制。

4.2 FFC

FFC(Flex Formatting Contexts)直译为"自适应格式化上下文",display值为flex或者inline-flex的元素将会生成自适应容器(flex container),可惜这个牛逼的属性只有谷歌和火狐支持,不过在移动端也足够了,至少safari和chrome还是OK的,毕竟这俩在移动端才是王道。

Flex Box 由伸缩容器和伸缩项目组成。通过设置元素的 display 属性为 flex 或 inline-flex 可以得到一个伸缩容器。设置为 flex 的容器被渲染为一个块级元素,而设置为 inline-flex 的容器则渲染为一个行内元素。

伸缩容器中的每一个子元素都是一个伸缩项目。伸缩项目可以是任意数量的。伸缩容器外和伸缩项目内的一切元素都不受影响。简单地说,Flexbox 定义了伸缩容器内伸缩项目该如何布局。

GFC 和 FFC 均为 css3 的属性。所以在移动端或者不考虑兼容低版本浏览器的情况下。可以愉快的使用。具体效果这里不做演示了,感兴趣的可以自己尝试。