BFC

BFC

今天我们来深入的了解一下 CSS 当中的 BFC 概念,关于这个东西在平常可能会经常听说到,但是它到底是一个什么样的设定呢?所以今天就抽些时间来深入的了解一下它,但是在此之前,我们先来看看 FC 的概念

FC

当然这里的 FC 指的并不是任天堂的红白机,而是 Formatting Contexts,它是 W3C CSS2.1 规范 中的一个概念,它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用

它与盒模型之间的关系可谓息息相关,所以我们就先来简单的了解一下盒模型,需要注意的是,本文当中只是浅显的了解一下 BFC 相关概念以及一些入门知识,想深入了解的可以参考上面的规范,里面有详细的描述

盒子模型的结构

盒子模型其实就是由以下 4 个盒子组成

  • content box,必备,由 content area 和 4 条 content/inner edge 组成
  • padding box,可选,由 padding 和 4 条 padding edge 组成,若 padding 宽度设置为 0,则 padding edge 与 content edage 重叠
  • border box,可选,由 border 和 4 条 border edge 组成,若 border 宽度设置为 0,则 border edge 与 padding edage 重叠
  • margin box,可选,由 margin 和 4 条 margin/outer edge 组成,若 margin 宽度设置为 0,则 margin edge 与 border edage 重叠

一般所说的盒模型有两种情况,一个是标准盒子模型,另一种是怪异盒子模型(IE 盒子模型),两者的区别就在于计算 width 和 height 的时候是否包含 padding/margin/border,从上面可以看出,margin、border、padding、content 分别定义了元素四种边,然后每种类型的边的四条边定义了一个盒子,分别是 content box、padding box、border box、margin box,而决定块盒在包含块中与相邻块盒的垂直间距的便是 margin-box,这个 margin-box 是始终存在的,即使它的 margin 为 0

比如一个元素 <div></div>,会生成一个块级的元素,同时元素也生成了一个块级盒子,如果不设置 div 的 margin 值,但是可以在浏览器生成的 computed style 中看到它的 margin 值仍然为 0 的,特别需要注意的是:当 <div></div> 标签被浏览器解析后会生成 div 元素并添加到 document tree 中,但 CSS 作用的对象并不是 document tree,而是根据 document tree 生成的 render tree,而盒子模型就是 render tree 的节点

  • CSS 作用的是盒子(box)而不是元素(element)
  • JavaScript 无法直接操作盒子

Box(盒子模型)是 CSS 布局的对象和基本单位,直观点来说,就是一个页面是由很多个 Box 组成的,元素的类型和 display 属性,决定了这个 Box 的类型,不同类型的 Box,会参与不同的 Formatting Context(一个决定如何渲染文档的容器),因此 Box 内的元素会以不同的方式渲染

Formatting Contexts

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

CSS2.1 中只有 BFC(Block Formatting Contexts) 和 IFC(Inline Formatting Contexts),CSS3 中还增加了 GFC(GridLayout Formatting Contexts) 和 FFC(Flex Formatting Contexts),本章当中我们主要介绍 BFC,其它几个我们在这里只是简单的了解

GFC

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

GFC 同 table 类似,同样是一个二维的表格,但 GridLayout 会有更加丰富的属性来控制行列,控制对齐以及更为精细的渲染语义和控制

FFC

FFC 直译为自适应格式化上下文,display 值为 flex 或者 inline-flex 的元素将会生成自适应容器(flex container),可惜这个属性只有谷歌和火狐支持,Flex Box 由伸缩容器和伸缩项目组成,通过设置元素的 display 属性为 flex 或 inline-flex 可以得到一个伸缩容器

设置为 flex 的容器被渲染为一个块级元素,而设置为 inline-flex 的容器则渲染为一个行内元素,伸缩容器中的每一个子元素都是一个伸缩项目,伸缩项目可以是任意数量的,伸缩容器外和伸缩项目内的一切元素都不受影响,简单地说,Flexbox 定义了伸缩容器内伸缩项目该如何布局

下面我们就来看我们本章将主要介绍的 BFC

BFC

BFC 直译为块级格式化上下文,它是一个独立的渲染区域,只有 block-level-box 参与, 它规定了内部的 block-level-box 如何布局,并且与这个区域外部毫不相干,块级元素会自动生成一个块级盒 block-level-box,这是块级盒 block-level-box 的盒模型构成,它表明的是块级盒自身的结构构成

我们从 BFC 的原理开始看起,简单来说其实也就是 BFC 的渲染规则,主要有下面几点

  • 普通流中的块元素(box)独占一行,然后从上往下一个接一个的排布(垂直方向),相邻元素间会有外边距折叠(垂直方向的距离由 margin 决定,属于同一个 BFC 的两个相邻 box 的 margin 会发生重叠)
  • 每个元素的 margin box 的左边, 与包含块 border box 的左边相接触(对于从左往右的格式化,否则相反)(即使存在浮动也是如此)
  • BFC 的区域不会与 float box 重叠
  • BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素(反之也如此)
  • 计算 BFC 的高度时,浮动元素也参与计算

BFC 的生成

既然 BFC 是一块渲染区域,那这块渲染区域到底在哪,它又是有多大,而这些则由生成 BFC 的元素决定(即哪些元素会生成 BFC),我们在这里也只列举一些比较常见的,完整的列表可以参考 MDN - 块格式化上下文

  • float 属性不为 none,意思是,只要设置了浮动,当前元素就创建了 BFC
  • overflow 的值不为 visible,可以让属性是 hidden、auto
  • position 为 absolute 或 fixed
  • display 为 inline-block,table-cell,table-caption,flex,inline-flex
    • 关于 display:table,之所以可以生成 BFC,主要原因在于 table 会默认生成一个匿名的 table-cell
    • 正是这个匿名的 table-cell 生成了 BFC
  • display 值为 flow-root 的元素,新属性,简单来说给有浮动元素的父容器添加该属性可以清除浮动
  • 弹性元素(display 为 flex 或 inline-flex 元素的直接子元素)
  • 网格元素(display 为 grid 或 inline-grid 元素的直接子元素)

BFC 的应用

有了上面这些规则,就可以用来解决我们平常遇到过的一些问题

两列布局

先看下面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<style>
body {
width: 300px;
position: relative;
}

.aside {
width: 100px;
height: 150px;
float: left;
background: #f66;
}

.main {
height: 200px;
background: #fcc;
}
</style>

<body>
<div class="aside"></div>
<div class="main"></div>
</body>

效果如下:

根据规则可知,虽然存在浮动的元素 aslide,但 main 的左边依然会与包含块的左边相接触,这是因为 BFC 不会与 float box 重叠,所以我们可以将 main 生成为 BFC 即可

1
2
3
.main {
overflow: hidden;
}

当触发 main 生成 BFC 后,这个新的 BFC 不会与浮动的 aside 重叠,这样就可以生成一个简单的两列布局,效果如下

高度坍塌

这个也是一个比较常见的问题,即内部元素设置了浮动以后,外面包裹的容器,比如 div 的高度会发生坍塌,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style>
.par {
border: 5px solid #fcc;
width: 300px;
}

.child {
border: 5px solid #f66;
width:100px;
height: 100px;
float: left;
}
</style>

<body>
<div class="par">
<div class="child"></div>
<div class="child"></div>
</div>
</body>

效果如下

发生这种情况的原因是因为内部的 child 元素使用了 float,使其脱离了文档流,故父元素的高度自然就没有了,解决办法有很多,原理只需要将父元素触发为 BFC 即可

1
2
3
.par {
overflow: hidden;
}

效果如下:

margin 重叠

这个也是平常开发当中经常会遇到的问题,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
p {
color: #f55;
background: #fcc;
width: 200px;
line-height: 100px;
text-align:center;
margin: 100px;
}
</style>

<body>
<p>Haha</p>
<p>Hehe</p>
</body>

效果如下

在控制台中审查元素可知,两个 p 之间的距离为 100px,发生了 margin 重叠,根据规则可知,属于同一个 BFC 的两个相邻 box 的 margin 会发生重叠,解决办法也很简单,我们可以给其中任意一个元素包裹一层容器,并触发该容器生成 BFC,那么此时的两个子元素就不属于同一个 BFC,所以就不会发生 margin 重叠的现象了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style>
.wrap {
overflow: hidden;
}

p {
color: #f55;
background: #fcc;
width: 200px;
line-height: 100px;
text-align:center;
margin: 100px;
}
</style>

<body>
<p>Haha</p>
<div class="wrap">
<p>Hehe</p>
</div>
</body>

效果如下

通过以上几个例子可以看出,同规则一样,BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素(反之也如此),因为 BFC 内部的元素和外部的元素绝对不会互相影响,因此当 BFC 外部存在浮动时,它不应该影响 BFC 内部 box 的布局,BFC 会通过变窄,而不与浮动有重叠

同样的,当 BFC 内部有浮动时,为了不影响外部元素的布局,BFC 计算高度时会包括浮动的高度,同样,避免 margin 重叠也是这样的一个道理

参考

# CSS

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×