一、前置概念
在正式介绍前,先让我们了解几个基本概念。
1.1 Box
简单来说,我们的web页面是由一个个Box组合而成的,而元素的类型和display 属性,决定了这个 Box 的类型 。
显示页面所有盒子,可以在控制台中输入:
[].forEach.call(document.querySelectorAll('*'), function(a){
a.style.outline = "1px solid red";
});
Box主要有四个部分:
margin (外边距)、border (边框)、padding (内边距)、content (内容区域)。
盒子是由margin
、border
、padding
、content
组成的,实际上每种类型的四条边定义了一个盒子,分别是分别是content box
、padding box
、border box
、margin box
,这四种类型的盒子一直存在,即使他们的值为0。
决定块盒在包含块中与相邻块盒的垂直间距的便是margin-box
。
Box
之间的距离虽然也可以使用padding
来控制,但是此时实际上还是属于box
内部里面,而且使用padding
来控制的话就不能再使用border
属性了。
不同类型的 Box,会参与不同的 Formatting Context,因此Box内的元素会以不同的方式渲染。
盒子类型
根据类型盒子可以分为:
盒子类型 | 属性及特性 | 参与FC |
---|---|---|
block-level box(块级盒) | display: block / list-item / table 的元素 | BFC |
inline-level box(行内级盒) | display: inline / inline-block / inline-table 的元素 | IFC |
flex container(弹性容器) | display: flex / inline-flex 的元素 | FFC |
grid container(栅格容器) | display: grid / inline-grid 的元素 | GFC |
1.2 常见定位方案
定位方案控制元素的布局,浏览器就会根据元素的盒类型和上下文对这些元素进行定位,有三种常见方案:
-
普通流 (normal flow)
在普通流中,元素按照其在 HTML 中的先后位置至上而下布局。- 块级元素:从上往下,垂直布局,独占一行。
- 行内元素 或 行内块元素:从左往右,水平布局,空间不够则自行换行。
- 当 position 为 static 或 relative、 float 为 none 时触发普通流。
-
浮动 (float)
在浮动布局中,元素首先按照普通流的位置出现,然后根据浮动的方向尽可能的向左边或右边偏移。- 左浮动的盒子向上向左排列
- 右浮动的盒子向上向右排列
- 浮动布局会导致普通流环绕在它的周边,除非设置 clear 属性
-
绝对定位 (absolute positioning)
在绝对定位布局中,元素会整体脱离普通流,因此绝对定位元素不会对其兄弟元素造成影响,而元素具体的位置由绝对定位的坐标决定, 绝对定位是以父元素的左上角原点为定位基准点 。- 绝对定位布局,盒从普通流中被移除,不影响常规流的布局
- 如果元素的属性
position
为absolute
或fixed
,它是绝对定位元素,它的定位相对于它的包含块,相关CSS属性:top
,bottom
,left
及right
; - 对于
position: absolute
,元素定位将相对于最近的一个relative
、fixed
或absolute
的父元素,如果没有则相对于body
1.3 外边距重叠
MDN:块的上外边距(margin-top)和下外边距(margin-bottom)有时合并(折叠)为单个边距,其大小为单个边距的最大值(或如果它们相等,则仅为其中一个),这种行为称为边距折叠。
注意:外边距重叠都是发生在块级元素中,设定 float 和 position=absolute 的元素不会产生外边距重叠行为。
有三种情况会形成外边距重叠:
(1)同一层相邻元素之间——相邻的两个元素之间的外边距重叠。
如下:box1和box2的margin为200px。
<style>
div {
width: 100px;
height: 100px;
background-color: pink;
}
.box1{
margin-bottom: 100px;
}
.box2{
margin-top: 200px;
}
</style>
<div class="box1"></div>
<div class="box2"></div>
效果:
(2)没有内容将父元素和后代元素分开
如果没有边框 border,内边距 padding,行内内容,也没有创建块级格式上下文就会出现父块元素和其内后代块元素外边界重叠,重叠部分最终会溢出到父级块元素外面。
如下:wrapper 有 100px 的上外边距,而 box 和 wrapper 之间是没有 100px 的上外边距。
<style>
.wrapper {
width: 200px;
height: 200px;
background-color: pink;
}
.box{
width: 100px;
height: 100px;
margin-top: 100px;
background-color: powderblue;
}
</style>
<div class="wrapper">
<div class="box"></div>
</div>
效果:
(3)空的块级元素
当一个块元素上边界margin-top
直接贴到元素下边界margin-bottom
时也会发生边界折叠。
如下示例,内容1 和 内容2 之间有200px的距离。
<style>
div {
margin-top: 100px;
margin-bottom: 200px;
}
</style>
<p>内容1</p>
<div></div>
<p>内容2</p>
效果:
二、FC(Formatting Context)
FC的全称是:Formatting Context
,译作格式化上下文,是W3C CSS2.1
规范中的一个概念。 它是页面中的一块渲染区域,并且有一套渲染规则,它决定了子元素如何定位,以及与其他元素的关系及相互作用。
格式化上下文主要包括四种类型:
-
BFC( Block Formatting Context | 块级格式化上下文)
-
IFC( Inline Formatting Context | 行内格式化上下文)
-
FFC( Flexible Formatting Context | 弹性盒格式化上下文)
-
GFC( Grids Formatting Context | 网格格式化上下文)
其中, CSS2.1中只有BFC和IFC,CSS3中才有GFC和FFC。GFC和FFC就是CSS3引入的新布局模型——grid布局和flex布局。
三、BFC
3.1 概念
BFC(Block Formatting Context) 块级-格式-上下文,属于定位方案中的普通流。
BFC 是 W3C CSS2.1 规范中的一个概念。引用W3C的官方解释:
BFC
它决定了元素如何对其内容进行定位,以及与其它元素的关系和相互作用,当涉及到可视化布局时,Block Formatting Context
提供了一个环境,HTML
在这个环境中按照一定的规则进行布局。
也就是说,BFC
是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。
具有BFC特性的元素可以看作隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素。
简单理解来看:BFC就是一个封闭的大箱子,箱子内部的元素无论如何变换,都不会影响到外面的元素。
3.2 BFC的渲染规则
BFC
中有特定的渲染规则,如下:
- 同一个BFC中两个相邻的元素的
margin重叠
问题 - BFC在计算高度时,即使浮动的元素也会参与高度计算
- BFC的区域不会与float的元素区域重叠
- 每个元素的左外边距与包含块的左边界相接触,即使浮动也如此
- 内部的盒子会一在垂直方向上一个个放置
3.3 如何触发BFC
只要元素满足下面任一条件即可触发 BFC 特性:
- html 元素——与生俱来的BFC
- 浮动元素:float 除 none 以外的值
- 绝对定位元素:position (absolute、fixed)
- display 为 inline-block、table-cells、flex
- overflow 除了 visible 以外的值 (hidden、auto、scroll)
3.4 BFC的应用
1. 同一个 BFC 下外边距会发生折叠
<head>
<style>
div {
width: 100px;
height: 100px;
background: lightblue;
margin: 100px;
}
</style>
</head>
<body>
<div></div>
<div></div>
</body>
从效果上看,因为两个 div 元素都处于同一个 BFC 容器下 ,所以第一个 div 的下边距和第二个 div 的上边距发生了重叠,所以两个盒子之间距离只有 100px,而不是 200px。
如果想要避免外边距的重叠,可以将其放在不同的 BFC 容器中。
<head>
<style>
.box {
overflow: hidden;
}
p {
width: 100px;
height: 100px;
background: lightblue;
margin: 100px;
}
</style>
</head>
<body>
<div class="box">
<p></p>
</div>
<div class="box">
<p></p>
</div>
</body>
这时候,两个盒子边距就变成了 200px
2. BFC 可以包含浮动的元素(清除浮动)
我们都知道,浮动的元素会脱离普通文档流,来看下下面一个例子
<head>
<style>
.box {
border: 2px solid #ccc;
}
.box-c {
width: 100px;
height: 100px;
background: lightblue;
float: left;
}
</style>
</head>
<body>
<div class="box">
<div class="box-c"></div>
</div>
</body>
由于容器内元素浮动,脱离了文档流,所以容器只剩下 4px 的边距高度。如果使触发容器的 BFC,那么容器将会包裹着浮动元素。
<style>
.box {
border: 2px solid #ccc;
overflow: hidden;
}
</style>
效果如图:
3. BFC 可以阻止元素被浮动元素覆盖
先来看一个文字环绕效果:
<head>
<style>
.box1 {
width: 100px;
float: left;
background: lightblue
}
.box2 {
background: pink
}
</style>
</head>
<body>
<div class="box1">我是box1,我是左浮动的元素</div>
<div class="box2">
我是box2。
凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字凑数文字
</div>
</body>
这时候其实第二个元素有部分被浮动元素所覆盖,(但是文本信息不会被浮动元素所覆盖) 如果想避免元素被覆盖,可触第二个元素的 BFC 特性,在第二个元素中加入 overflow: hidden,就会变成:
<style>
.box2 {
background: pink;
overflow: hidden;
}
</style>
这个方法可以用来实现两列自适应布局。
3.5 小结
通过以上的几个例子我们可以看出:
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
四、扩展
4.1 IFC
IFC(Inline Formatting Contexts) 直译为"内联格式化上下文"。 它是 一个块级元素中仅包含内联级别元素 。
IFC布局规则
- 在一个行内格式化上下文中,从包含块的顶部开始,盒是一个接一个水平放置的。
- 每个盒之间的水平margin,border和padding都有效,垂直方向上的样式空间不会被计算。
- 盒可能以不同的方式竖直对齐:以它们的底部或者顶部对齐,或者以它们里面的文本的基线对齐。
- 行内块级元素之间默认留有间隙。
IFC的作用
- 水平居中:当一个块要在环境中水平居中时,设置其为 inline-block 则会在外层产生 IFC,通过 text-align则可以使其水平居中。
- 垂直居中:创建一个 IFC,用其中一个元素撑开父元素的高度,然后设置其 vertical-align:middle,其他行内元素则可以在此父元素下垂直居中。
4.2 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.3 FFC
FFC(Flex Formatting Contexts)直译为"自适应格式化上下文"。display 值为 flex 或者 inline-flex 的元素将会生成自适应容器(flex container)。 Flex Box 由伸缩容器和伸缩项目组成。通过设置元素的 display 属性为 flex 或 inline-flex 可以得到一个伸缩容器。
布局规则
- 设置为 flex 的容器被渲染为一个块级元素。
- 设置为 inline-flex 的容器则渲染为一个行内元素。
- 伸缩容器中的每一个子元素都是一个伸缩项目。伸缩项目可以是任意数量的。伸缩容器外和伸缩项目内的一切元素都不受影响。简单地说,Flexbox 定义了伸缩容器内伸缩项目该如何布局。
FFC与BFC的区别
FFC与BFC有点儿类似,但仍有以下几点区别:
- Flexbox 不支持 ::first-line 和 ::first-letter 这两种伪元素。
- vertical-align 对 Flexbox 中的子元素,是没有效果的。
- float 和 clear 属性对 Flexbox 中的子元素是没有效果的,也不会使子元素脱离文档流。(但是对Flexbox 是有效果的!)
- 多栏布局(column-*) 在 Flexbox 中也是失效的,就是说我们不能使用多栏布局在 Flexbox 排列其下的子元素
- Flexbox 下的子元素不会继承父级容器的宽。