BFC

129 阅读5分钟

什么是BFC?

在了解BFC之前,先了解一下FC的定义。FC(Formatting Context 格式化上下文),FC指定了页面中一块独立区域的渲染规则,这个渲染规则可以理解为该元素内部的布局规则,不同的FC有不同的规则定义。在CSS Display Module Level 4中,元素的inner display指定了这个规则。BFC就是其中一种。

the inner display type, which defines (if it is a non-replaced element) the kind of formatting context it generates, dictating how its descendant boxes are laid out. (The inner display of a replaced element is outside the scope of CSS.)

目前网页中几种FC的分类:

  • IFC(Inline Formatting Context):行级格式化上下文
  • BFC(Block Formatting Context):块格式化上下文
  • FFC(Flex Formatting Context):自适应格式化上下文
  • GFC(Grid Formatting Context):网格布局格式化上下文

BFC的触发条件

在以下几种情况下,会触发形成BFC

  • 根元素HTML
  • 浮动元素:float 属性不为none
  • position定位:设置为 absolutefixed
  • overflow 属性:设置为非visibleclip元素
  • display属性:
    • 设置为 table类型:
      • table<table>元素默认值
      • table-caption:HTML 表格标题默认值
      • table-row<tr>元素默认值
      • table-row-group<tbody>元素默认值
      • table-header-group<thead>元素默认值
      • table-footer-group<tfoot>元素默认值
      • table-cell:HTML 表格单元格默认值
      • inline-table
    • 设置为inline-block
    • 设置为flow-root
    • 设置为flex类型:父元素display属性为flex 或者 inline-flex,其子元素内部的布局规则为BFC(其子元素本身非table类型、grid类型、flex类型)
    • 设置为grid类型:父元素display属性为gird 或者 inline-grid,其子元素内部的布局规则为BFC(其子元素本身非table类型、grid类型、flex类型)
  • contain 属性:
    • layout
    • content
    • paint
  • 多列容器(暂时没有搞懂)

注意,根据display的值可以看出,单纯的display:block的情况下,并不会形成BFC。下面的实例验证一(父容器非BFC的案例)可以证明这一点。

BFC渲染规则

  • 渲染隔离:一个BFC是页面上的一个隔离的独立容器,容器里面的子元素的排版布局不会直接影响到外面的元素(可能通过子元素影响父元素容器大小间接影响外部元素布局)。
  • 容器大小计算:区域大小由内部子元素中的最外边界确定:
    • 即一个BFC容器会包裹所有的子元素块,无论该子元素是否是浮动元素,所以BFC的高度计算会包含float元素。
  • margin重叠:
    • 在同一个BFC中,相邻元素形成的box的上下margin产生重叠;
    • 但在不同的BFC中,各自的子元素的margin不重叠。
  • 排布方向:
    • 内部的子元素由上到下依次排布(flow布局)。
    • 内部子元素根据书写方向(writing-mode)确定从左开始排布还是从右开始排布。
    • 起始位置的子元素的margin紧贴父容器的border(只有起始方向的margin紧贴)。
      • 例如如果writing-mode: horizontal-tb是从左开始排布元素,那么起始子元素的margin与父元素的左border对齐。
  • 当前的BFC不与外部的float box重叠。

实例验证

1、容器大小计算

区域大小由内部元素的边界确定,高度计算包含float元素。

当不给父容器设置高度的情况下,查看父容器的高度是如何计算的:

 <!-- bfc的高度计算会包含float子元素 可以用作清除内部浮动 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <title>BFC</title>
    <style>
        .bfc {
            display: flow-root;
        }
        .container {
            border: 1px solid #939393;
            background: #f2f2f2;
            margin-bottom: 20px;
        }
        .float {
            float: left;
            background-color: #e98f53;
        }
        .child {
            height: 40px;
            width: 40px;
        }  
    </style>
</head>

<body>
	<!-- 父容器非BFC -->
        <!-- 证明单纯的display:block的情况下,并不会形成BFC。-->
    <div class="container">
        <div class="float child"></div>
    </div>

	<!-- 父容器是BFC -->
    <div class="bfc container" style="margin-top: 100px">
        <div class="float child"></div>
    </div>
</body>
</html>
1.png

2、margin重叠

同一个BFC中相邻的元素上下margin重叠

当给子元素设置

<!DOCTYPE html>
<html lang="en">
<head>
    <title>BFC</title>
    <style>
        .bfc {
            display: flow-root;
        }
        .container {
            border: 1px solid #939393;
            background: #f2f2f2;
        }
       .bfc-child {
            height: 40px;
            margin: 30px 0;
            background-color: #ccc;
       }
    </style>
</head>

<body>
	<div class="bfc container">
        <div class="bfc-child"></div>
    </div>
    <div class="bfc container">
        <div class="bfc-child"></div>
        <div class="bfc-child"></div>
    </div>
</body>
</html>
2.png

相反,当父容器非BFC时,所有子元素(无论是否是平级)都会发生上下margin重叠。 将上面父容器的display属性改为contents

contents

These elements don't produce a specific box by themselves. They are replaced by their pseudo-box and their child boxes.

当设置元素为content时,该元素不会生成自己的box,而是会被子元素生成的box取代。

.bfc{
	display: contents;
}
3.png

会发现此时处于两个父级下的子元素,上下margin也会发生重叠。此时因为父容器不是BFC,没有渲染隔离,所有子元素生成的盒子被平铺到了最外层。

3、当前的BFC不与外部的float box重叠

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BFC</title>
    <style>
	.bfc {
            display: flow-root;
        }
        .float {
            float: left;
            background-color: #e98f53;
            height: 40px;
            width: 40px;
        }
        .aside {
            width: 200px;
            height: 200px;
            background-color: #696fa7;
        }
    </style>
</head>

<body>
    <!-- 规则三:bfc区域不和浮动元素重叠 -->
    <div class="float"></div>
    <div class="aside">该元素非BFC,则该元素和小黄块重叠</div>

    <div class="float"></div>
    <div class="aside bfc">该元素BFC,则该元素和小黄块不重叠</div>

</body>

</html>

4.png

BFC的作用

利用BFC的渲染规则,可以实现以下几个功能:

  • 自适应两栏布局
  • 阻止元素被浮动元素覆盖
  • 清除内部浮动
  • 阻止margin重叠:分属于不同的BFC时不发生重叠

后面三条在实验中以及实现过了,但是针对第一条,如何利用BFC实现两栏布局呢?

利用2个特性:

  1. 块级元素width:auto 时,会自动拉伸至父元素左右边界处(横向占满)。
  2. BFC渲染规则:当前的BFC不与外部的float box重叠。
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BFC</title>
    <style>
        .bfc {
            display: flow-root;
        }
				.container {
            border: 1px solid #939393;
            background: #f2f2f2;
            margin-bottom: 20px;
        }
        .main{
            height: 300px;
            background: #ddd;
        }
        .float-aside{
            width: 100px;
            height: 150px;
            float: left;
            background: #757575;
        }
    </style>
</head>

<body>
    <!-- 利用bfc区域不和浮动元素重叠的特性,实现自适应两栏布局 -->
    <div class="bfc container">
        <div class="float-aside"></div>
        <div class="bfc main">设置float的宽度,然后给相邻的bfc元素不设置宽度,块级元素默认宽度为auto,那么该bfc元素会自动延伸到父元素的最右端</div>
    </div>
</body>
</html>
5.png

参考