CSS盒模型、BFC和清除浮动

在 CSS 中,所有的元素都被一个个的“盒子(box)”包围着,理解这些“盒子”的基本原理,是我们使用CSS实现准确布局、处理元素排列的关键。

块级盒子(Block box) 和 内联盒子(Inline box)

在 CSS 中我们广泛地使用两种“盒子” —— 块级盒子 (block box) 和 内联盒子 (inline box)。这两种盒子会在页面流(page flow)和元素之间的关系方面表现出不同的行为:

一个被定义成块级的(block)盒子会表现出以下行为:

  • 盒子会在内联的方向上扩展并占据父容器在该方向上的所有可用空间,在绝大数情况下意味着盒子会和父容器一样宽
  • 每个盒子都会换行
  • widthheight 属性可以发挥作用
  • 内边距(padding), 外边距(margin) 和 边框(border) 会将其他元素从当前盒子周围“推开”

除非特殊指定,诸如标题(<h1>等)和段落(<p>)默认情况下都是块级的盒子。

如果一个盒子对外显示为 inline,那么他的行为如下:

  • 盒子不会产生换行
  • widthheight 属性将不起作用。
  • 垂直方向的内边距、外边距以及边框会被应用但是不会把其他处于 inline 状态的盒子推开。
  • 水平方向的内边距、外边距以及边框会被应用且把其他处于 inline 状态的盒子推开。

用做链接的 <a> 元素、 <span><em> 以及 <strong> 都是默认处于 inline 状态的。

我们通过对盒子display 属性的设置,比如 inline 或者 block ,来控制盒子的外部显示类型。

补充: 内部和外部显示类型

在这里最好也解释下内部外部 显示类型。如上所述, css的box模型有一个外部显示类型,来决定盒子是块级还是内联。

同样盒模型还有内部显示类型,它决定了盒子内部元素是如何布局的。默认情况下是按照正常文档流布局,也意味着它们和其他块元素以及内联元素一样(如上所述)。

但是,我们可以通过使用类似 flexdisplay 属性值来更改内部显示类型。 如果设置 display: flex,在一个元素上,外部显示类型是 block,但是内部显示类型修改为 flex。 该盒子的所有直接子元素都会成为flex元素,会根据弹性盒子(Flexbox)规则进行布局。

什么是CSS 盒模型?

完整的 CSS 盒模型应用于块级盒子,内联盒子只使用盒模型中定义的部分内容。模型定义了盒的每个部分 —— margin, border, paddingcontent —— 合在一起就可以创建我们在页面上看到的内容。为了增加一些额外的复杂性,有一个标准的和替代(IE)的盒模型。

标准盒模型和IE盒子模型

在 CSS 盒子模型 (Box Model) 规定了元素处理元素的几种方式:

  • width和height:内容的宽度、高度(不是盒子的宽度、高度)。
  • padding:内边距。
  • border:边框。
  • margin:外边距。

CSS盒模型和IE盒模型的区别:

  • 标准盒子模型中,width 和 height 指的是内容区域的宽度和高度。增加内边距、边框和外边距不会影响内容区域的尺寸,但是会增加元素框的总尺寸。

    5mcYjg.jpg

  • IE盒子模型中,width 和 height 指的是内容区域+border+padding的宽度和高度。

    5mcJgS.jpg

CSS如何设置这两种模型

1
2
3
4
/* 设置当前盒子为 标准盒模型(默认) */
box-sizing: content-box;
/* 设置当前盒子为 IE盒模型 */
box-sizing: border-box;

margin塌陷/margin重叠

标准文档流中,竖直方向的margin不叠加,只取较大的值作为margin(水平方向的margin是可以叠加的,即水平方向没有塌陷现象)。且只会发生在块级元素,行内元素和行内块元素不会出现。

PS:如果不在标准流,比如盒子都浮动了,那么两个盒子之间是没有margin重叠的现象的。

我们来看几个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!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>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
.father{
background: green;
/* overflow: hidden; */
}
.son{
height: 50px;
margin-top: 50px;
margin-bottom: 20px;
background: blue;
}
.brother{
height: 50px;
margin-top: 30px;
background: red;
}


</style>
</head>

<body>
<div class="father">
<div class="son"></div>
<div class="brother"></div>
</div>
</body>

</html>

上面的代码中,儿子和兄弟的height是 100px,儿子magin-top 是50px,margin-bottom是20px,兄弟magin-top 是30px。竖直方向的margin不叠加,只取较大的值作为margin,所以中间20px塌陷在30px中。另外,此时父亲的height 是130px,而不是180px。因为儿子和父亲在竖直方向上为一个margin

5mxrPx.png
5mx0aR.png

如果我们给父亲设置一个属性:overflow: hidden,就可以避免这个问题,此时父亲的高度是180px,这个用到的就是BFC

或者为父元素加一个border: 2px solid red

margin这个属性,本质上描述的是兄弟和兄弟之间的距离; 最好不要用这个marign表达父子之间的距离。

所以,如果要表达父子之间的距离,我们一定要善于使用父亲的padding,而不是儿子的margin。

BFC(边距重叠解决方案)

BFC的概念

块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。

BFC 的原理/BFC的布局规则【非常重要】

BFC 的原理,其实也就是 BFC 的渲染规则(能说出以下四点就够了)。包括:

  • BFC 内部的子元素,在垂直方向,边距会发生重叠

  • BFC在页面中是独立的容器,外面的元素不会影响里面的元素,反之亦然。(举例1:解决 margin重叠

  • BFC区域不与旁边的float box区域重叠。(举例2:解决浮动带来的影响

  • 计算BFC的高度时,浮动的子元素也参与计算。(清除浮动,解决高度塌陷问题

如何生成BFC

有以下几种方法:

  • 方法1:overflow: 不为visible,可以让属性是 hidden、auto。【最常用】

  • 方法2:浮动中:float的属性值不为none。意思是,只要设置了浮动,当前元素就创建了BFC

  • 方法3:绝对定位:posiitonabsolutefixed

  • 方法4:displayinline-block, table-cell, table-caption, flex, inline-flexgrid

BFC 的应用

举例1:解决 margin重叠

当父元素和子元素发生 margin 重叠时,解决办法:给子元素或父元素创建BFC

比如说,针对下面这样一个 div 结构:

1
2
3
<div class="father">
<p class="son"></p>
</div>

上面的div结构中,如果父元素和子元素发生margin重叠,我们可以给子元素创建一个 BFC,就解决了:

1
2
3
<div class="father">
<p class="son" style="overflow: hidden"></p>
</div>

因为第二条:BFC区域是一个独立的区域,不会影响外面的元素

举例2:BFC区域不与float区域重叠:

针对下面这样一个div结构;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>

.father-layout {
background: pink;
}

.father-layout .left {
float: left;
width: 100px;
height: 100px;
background: green;
}

.father-layout .right {
height: 150px; /*右侧标准流里的元素,比左侧浮动的元素要高*/
background: red;
}

</style>
</head>
<body>

<section class="father-layout">
<div class="left">
左侧,1234
</div>
<div class="right">
右侧,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,
</div>
</section>

</body>
</html>

效果如下:

5nFuEF.png

上图中,由于右侧标准流里的元素,比左侧浮动的元素要高,导致右侧有一部分会跑到左边的下面去。

如果要解决这个问题,可以将右侧的元素创建BFC,因为第三条:BFC区域不与float box区域重叠。解决办法如下:(将right区域添加overflow属性)

1
2
3
<div class="right" style="overflow: hidden">
右侧,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,smyhvae,
</div>

5nFKN4.png

上图表明,解决之后,father-layout的背景色显现出来了,说明问题解决了。

清除浮动

高度塌陷

在浮动布局中,父元素的高度默认是被子元素撑开的,当子元素浮动后,其会完全脱离文档流

子元素从文档流中脱离,将会无法撑起父元素的高度,导致父元素的高度丢失

父元素高度丢失以后,其下的元素会自动上移,导致页面的布局混乱

现在有下面这样的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!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>

.father {
background: pink;
/* overflow: hidden; */
}

.son {
float: left;
background: green;
}

</style>
</head>
<body>
<section class="father">
<div class="son">
hello son
</div>
</section>
</body>
</html>

效果如下:

5niapn.png

上面的代码中,儿子浮动了,但由于父亲没有设置高度,导致看不到父亲的背景色(此时父亲的高度为0)。

如何清除浮动带来的影响?

给父亲设置高度。

正所谓有高度的盒子,才能关住浮动

伪元素+clear

clear 属性指定一个元素是否必须移动(清除浮动后)到在它之前的浮动元素下面

clear 属性适用于浮动和非浮动元素。

当应用于非浮动块时,它将非浮动块的边框边界移动到所有相关浮动元素外边界下方。这个非浮动块的垂直外边距会折叠

另一方面,两个浮动元素的垂直外边距将不会折叠

当应用于浮动元素时,它将元素的外边界移动到所有相关的浮动元素外边框边界下方。这会影响后面浮动元素的布局,后面的浮动元素的位置无法高于它之前的元素。

要被清除的相关浮动元素指的是在相同块级格式化上下文中的前置浮动

具体方法:

  • 浮动元素的容器添加一个clearfix的class。
  • 然后给这个class添加一个::after(::after用来创建一个伪元素,作为已选中元素的最后一个子元素),实现浮动元素之后添加一个看不见的块元素(Block element)清理浮动。
1
2
3
4
5
<section class="father clearfix">
<div class="son">
hello son
</div>
</section>
1
2
3
4
5
.clearfix::after{
content: '';
display: block; /* 确保该元素是一个块级元素 */
clear: both;/*清除浮动*/
}

BFC

给父亲增加 overflow=hidden属性即可, 增加之后,效果如下:

5niNfs.png

为什么父元素成为BFC之后,就有了高度呢?

这就回到了第四条:计算BFC的高度时,浮动元素也参与计算。意思是,在计算BFC的高度时,子元素的float box也会参与计算