BFC

BFC

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

FC

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

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

盒子模型的结构

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

  • content box,必备,由 content area4content/inner edge 组成
  • padding box,可选,由 padding4padding edge 组成,若 padding 宽度设置为 0,则 padding edgecontent edage 重叠
  • border box,可选,由 border4border edge 组成,若 border 宽度设置为 0,则 border edgepadding edage 重叠
  • margin box,可选,由 margin4margin/outer edge 组成,若 margin 宽度设置为 0,则 margin edgeborder edage 重叠

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

比如一个元素 <div></div>,会生成一个块级的元素,同时元素也生成了一个块级盒子,如果不设置 divmargin 值,但是可以在浏览器生成的 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 ContextsW3C CSS2.1 规范(见开头部分)中的一个概念,它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用,最常见的 Formatting contextBFCIFC

CSS2.1 中只有 BFCBlock Formatting Contexts) 和 IFCInline Formatting Contexts),CSS3 中还增加了 GFCGridLayout Formatting Contexts) 和 FFCFlex Formatting Contexts),本章当中我们主要介绍 BFC,其它几个我们在这里只是简单的了解

GFC

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

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

FFC

FFC 直译为自适应格式化上下文,display 值为 flex 或者 inline-flex 的元素将会生成自适应容器(flex container),可惜这个属性只有谷歌和火狐支持,Flex Box 由伸缩容器和伸缩项目组成,通过设置元素的 display 属性为 flexinline-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 的两个相邻 boxmargin 会发生重叠)
  • 每个元素的 margin box 的左边, 与包含块 border box 的左边相接触(对于从左往右的格式化,否则相反)(即使存在浮动也是如此)
  • BFC 的区域不会与 float box 重叠
  • BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素(反之也如此)
  • 计算 BFC 的高度时,浮动元素也参与计算

BFC 的生成

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

  • float 属性不为 none,意思是,只要设置了浮动,当前元素就创建了 BFC
  • overflow 的值不为 visible,可以让属性是 hiddenauto
  • positionabsolutefixed
  • displayinline-blocktable-celltable-captionflexinline-flex
    • 关于 display:table,之所以可以生成 BFC,主要原因在于 table 会默认生成一个匿名的 table-cell
    • 正是这个匿名的 table-cell 生成了 BFC
  • display 值为 flow-root 的元素,新属性,简单来说给有浮动元素的父容器添加该属性可以清除浮动
  • 弹性元素(displayflexinline-flex 元素的直接子元素)
  • 网格元素(displaygridinline-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 的两个相邻 boxmargin 会发生重叠,解决办法也很简单,我们可以给其中任意一个元素包裹一层容器,并触发该容器生成 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

×