今天我们来深入的了解一下 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 | <style> |
效果如下:
根据规则可知,虽然存在浮动的元素 aslide
,但 main
的左边依然会与包含块的左边相接触,这是因为 BFC
不会与 float box
重叠,所以我们可以将 main
生成为 BFC
即可
1 | .main { |
当触发 main
生成 BFC
后,这个新的 BFC
不会与浮动的 aside
重叠,这样就可以生成一个简单的两列布局,效果如下
高度坍塌
这个也是一个比较常见的问题,即内部元素设置了浮动以后,外面包裹的容器,比如 div
的高度会发生坍塌,代码如下
1 | <style> |
效果如下
发生这种情况的原因是因为内部的 child
元素使用了 float
,使其脱离了文档流,故父元素的高度自然就没有了,解决办法有很多,原理只需要将父元素触发为 BFC
即可
1 | .par { |
效果如下:
margin 重叠
这个也是平常开发当中经常会遇到的问题,代码如下
1 | <style> |
效果如下
在控制台中审查元素可知,两个 p
之间的距离为 100px
,发生了 margin
重叠,根据规则可知,属于同一个 BFC
的两个相邻 box
的 margin
会发生重叠,解决办法也很简单,我们可以给其中任意一个元素包裹一层容器,并触发该容器生成 BFC
,那么此时的两个子元素就不属于同一个 BFC
,所以就不会发生 margin
重叠的现象了
1 | <style> |
效果如下
通过以上几个例子可以看出,同规则一样,BFC
就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素(反之也如此),因为 BFC
内部的元素和外部的元素绝对不会互相影响,因此当 BFC
外部存在浮动时,它不应该影响 BFC
内部 box
的布局,BFC
会通过变窄,而不与浮动有重叠
同样的,当 BFC
内部有浮动时,为了不影响外部元素的布局,BFC
计算高度时会包括浮动的高度,同样,避免 margin
重叠也是这样的一个道理