清除和去除浮动

清除和去除浮动

本文主要讲述了CSS的清除浮动的原理,并讲述了清除和包含浮动的主要方法。

浮动的原理

浮动和定位一样会以某种方式将元素从文档的正常流中删除,而且会影响布局。一个元素浮动时,其他内容会”环绕”该元素。

浮动的特点

  • 浮动元素周围的外边距不会合并(其实是浮动元素形成了新的BFC ,后续讲解)
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
<style>
.box{
width: 100%;
height: 200px;
background: #ececdd;
margin-left: auto;
margin-right: auto;
}

.inner-nofloat {
width: 100%;
height: 20px;
background: #f7c3c3;
margin-bottom: 40px;
margin-top: 20px; /*两个nofloat之间的外边距是40px,进行了合并处理*/
}

.inner-float {
width: 100%;
height: 20px;
background: #d3cff7;
float: left;
margin-bottom: 40px; /*float和nofloat之间的外边距是60px,并没有进行合并处理*/
margin-top: 20px;
}
</style>

<div class="box">
<div class="inner-nofloat"></div>
<div class="inner-nofloat"></div>
<div class="inner-float"></div>
</div>
  • 浮动元素的包含块是其最近的祖先元素
  • 浮动元素会生成一个块级框即使元素本身是行内元素,因此没有必要给浮动元素增加display:block代码

浮动的规则

  • 浮动元素的左(右)外边界不能超出其包含块的左(右)内边界,即左浮动元素的左外边界向左最远只能到达其包含块的左内边界,右浮动元素类似。
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
<style>
.box{
width: 90%;
height: 200px;
background: #ececdd;
margin-left: auto;
margin-right: auto;
}

.inner-float-left {
width: 100%;
height: 20px;
background: #d3cff7;
float: left;
margin-bottom: 40px;
margin-top: 20px;
}

.inner-float-right {
width: 100%;
height: 20px;
background: #d3cff7;
float: right;
margin-bottom: 40px;
margin-top: 20px;
}
</style>

<div class="box">
<div class="inner-float-left"></div>
<div class="inner-float-right"></div>
</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
<style>
.box{
border: 2px solid #c9f6f2;
width: 100%;
background: #ececdd;
margin-left: auto;
margin-right: auto;
margin-bottom: 50px;
}

.inner-nofloat {
width: 100%;
height: 200px;
background: #f7c3c3;
margin:20px 0;
}

.inner-float {
width: 100%;
height: 200px;
background: #d3cff7;
float: left;
margin:20px 0;
}
</style>

<div class="box">
<div class="inner-nofloat"></div>
</div>
<div class="box">
<div class="inner-float"></div>
</div>


如果元素不浮动,那么父块级元素默认会包含子块级元素,从而使子元素显示在父元素之内,但是一旦子元素浮动,那么父元素将不会包含子元素,因为浮动元素不在正常流中,而它的父元素在正常流中,因此处于正常流的父元素并不能包含子浮动元素,从而导致父元素在没有其他子元素且没有设置高度的情况下会产生高度塌陷CSS2.1澄清了浮动元素行为的一个方面:浮动元素会延伸,从而包含其所有后代浮动元素(其实还是因为浮动元素形成了新的BFC,后续讲解)。所以通过设置第二个box为浮动元素,可以把浮动元素包含在其box内。


那么如果这个父元素的包含块仍然不是浮动元素也可能导致包含块的高度塌陷!

清除浮动的原理

利用清除可以设置元素禁止浮动元素出现在它的左侧、右侧甚至是双侧。

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
<style>
div {
display: inline-block;
width: 40%;
height: 100px;
background: #d3cff7;
margin-right: 10px;
margin-bottom : 0;
padding: 0;
}

.float {
float: left;
}

.box {
height: 50px;
}
p {
border: 1px solid black;
margin:0;
padding:0;
width: 80%;
}
</style>

<div class="float"></div>
<div class="box"></div>
<p>3333333333333333333333333333333333333333333</p>
1
2

3333333333333333333333333333333333333333333

由于div1浮动,pdiv1布局重叠,为了解决这个问题,可以使p元素左侧不允许有浮动元素,给p元素添加一个clear:left的样式之后

1
2

3333333333333333333333333333333333333333333


可以发现p的位置下移从而使pdiv1布局不会重叠,其实是使用清除之后,自动增加了p元素的上外边距,确保它落在浮动元素div1的下面。CSS2.1引入了清除区域的概念,清除区域是在元素上外边距之上增加的额外间隔(确保浮动元素不会与该元素重叠),不允许浮动元素进入这个范围,意味着设置clear属性的p元素的外边距并不改变,之所以该元素会向下移动是因为清除区域造成的。div1div2的高度分别是100px50px, 因此清除区域的高度在50px左右(不算border和浏览器代理初始样式等)。如果给p元素一个上外边距margin-top:30px,则p元素并不会向下移动30px,而是在解析完样式之后清除区域的高度变成了50px - 30px左右,而p元素仍然位置不变,如果要使p元素下移,则需要使p元素的margin-top值高于最大的清除区域高度50px,并且向下位移的距离也是margin-top值减去最大的清除区域高度。

通常情况下,我们当然很难准确计算最大的清除区域高度,因此要使浮动元素div1p元素有一个确定的值很难通过设置p元素的上外边距获取效果,而是应该设置div1的下外边距margin-bottom(仍然是因为浮动元素形成了BFC上下文)。例如需要确定浮动元素和p元素上下距离为15px

1
2

3333333333333333333333333333333333333333333

清除浮动的方法

浮动容易导致块级父元素的高度塌陷,通过直接设置父元素的高度以及设置父元素为浮动元素并不能很好的解决元素浮动带来的布局问题,因此可以通过clear属性来清除浮动。

追加元素并设置clear属性

  • 优势:简单,布局灵活,浏览器兼容性好
  • 弊端:结构和表现混淆,添加了不必要的DOM元素,不便于后期维护

如果父元素高度塌陷,则可以通过在父元素的尾部追加空的子元素,并利用clear:both解决塌陷问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<style>
.box{
border: 2px solid #c9f6f2;
width: 100%;
background: #ececdd;
margin-left: auto;
margin-right: auto;
margin-bottom: 50px;
}
.float {
width: 25%;
height: 100px;
float: left;
background: #d3cff7;
margin:10px;
}
</style>

<div class="box">
<div class="float"></div>
<div class="float"></div>
<div class="float"></div>
</div>





如果父元素没有设置高度,那么因为三个浮动子元素脱离正常流导致父元素的高度塌陷,如果给第三个浮动子元素加上clear:both的样式,并没有解决高度塌陷的问题,因为非浮动父元素仍然不能包含浮动子元素。


此时如果给父元素的尾部添加一个非浮动子元素并给出样式clear:both,由于这个子元素是非浮动元素,通过清除的原理可以知道相对之前的浮动元素,该非浮动元素会生成一个清除区域,这个清除区域的高度可以促使非浮动元素处于浮动元素的下方,而且非浮动元素和浮动元素之间的准确边距通常由浮动元素的下边距确定,因此导致这个非浮动元素处于三个浮动子元素的下方10px处(三个浮动元素的样式margin:10px导致),又因为父元素包含了这个非浮动子元素,从而使父元素也可以包含浮动元素,解决了高度塌陷的问题(当然添加的非浮动子元素一般都是空元素,例如br)。

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
<style>

.box{
...
}
.float {
...
}

.nofloat {
width: 100%;
height: 20px;
background: #f5d5ea;
margin:0;
padding:0;
clear: both;
}
</style>

<div class="box">
<div class="float"></div>
<div class="float"></div>
<div class="float"></div>
<div class="nofloat"></div>
</div>

使用CSS样式插入元素

  • 优势:不破坏文档结构,没有副作用
  • 劣势:使用display:block会使父子元素的垂直外边距重叠(后续详细说明)

原理其实和追加元素并设置clear属性相同,只是使用css样式来处理。

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
<style>
.box{
border: 2px solid #c9f6f2;
width: 100%;
background: #ececdd;
margin-left: auto;
margin-right: auto;
margin-bottom: 50px;
}

.clearfix:after {
content: '';
display: block;
clear: both;
}

.float {
width: 25%;
height: 100px;
float: left;
background: #d3cff7;
margin:10px;
}
</style>

<div class="box clearfix">
<div class="float"></div>
<div class="float"></div>
<div class="float"></div>
</div>

包含浮动的原理

解决浮动元素带来布局的副作用的方法之一是使用clear清除浮动,但是也可以利用BFC的特性来包含浮动从而解决父元素高度塌陷的问题。

什么是BFC

BFC(Block formatting context)直译为”块级格式化上下文”。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。 block-level boxdisplay属性为block, list-item, table的元素,会生成block-level box,并且参与block fomatting context。 具体可以从什么是BFC中了解BFC的特性。

BFC特性

  • BFC会阻止垂直外边距(margin-topmargin-bottom)折叠(属于同一个BFC的两个相邻Boxmargin会发生重叠 )
  • BFC不会重叠浮动元素
  • BFC可以包含浮动(计算BFC的高度时,浮动元素的高度也参与计算 ,可以利用BFC的这个特性来“清浮动”,应该说包含浮动。也就是说只要父容器形成BFC就可以)

需要注意的是根元素本身就能触发一个BFC,事实上除了根元素以外以下的方式也能触发BFC

  • float (left,right)
  • overflow 除了visible 以外的值(hidden,auto,scroll)
  • display (table-cell,table-caption,inline-block)
  • position(absolute,fixed)

BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之也如此。意思是可以把能创建BFC的盒子当作那个根元素,同时这个创建了BFC的盒子则是一个独立的容器,里面参与BFC的块级盒不会影响到盒子外面的盒子,外面的盒子也不会影响到里面参与了BFC的块级盒。

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
<style>
.box {
width: 100%;
}
.bfc-float-box1 {
float: left;
width: 100%;
background: #ececdd;
}

.bfc-float-box2 {
float: left;
width: 100%;
background: #f9d7f7;
}


.bfc-inner-box1 {
margin-bottom: 30px;
width: 100px;
height: 100px;
background: #f7c3c3;
}

.bfc-inner-box2 {
margin-top: 50px;
width: 100px;
height: 100px;
background: #d3cff7;
}
</style>

<div class="box" style="width: 100%;">
<div class="bfc-float-box1">
<div class="bfc-inner-box1"></div>
</div>
<div class="bfc-float-box2">
<div class="bfc-inner-box2"></div>
</div>
</div>

两个float的父盒子在为它下面的盒子创建了一个BFC,从而将float盒子里面的子盒子给隔离了起来,因此也就不会margin折叠了。所以上下边距30px50px并不会重叠,因为不属于同一个BFC,当然在同一个BFC内边距还是会重叠的。同时可以发现每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<style>
.box {
width: 100%;
}

.bfc-float-box1 {
float: left;
width: 100px;
height: 100px;
background: #ececdd;
}

.bfc-overflow-box2 {
overflow: hidden;
width: 100px;
height: 100px;
background: #d3cff7;
}
</style>

<div class="box">
<div class="bfc-float-box1">float</div>
<div class="bfc-overflow-box2">overflow</div>
</div>
float
overflow

BFC不会重叠浮动元素,由于第二个div使用overflow: hidden触发了新的BFC,因此它被排布在float元素的右边而不会重叠。
需要注意的是形成新BFC上下文中的块级盒的表现形式和根元素下(根元素也也会触发BFC)的块级盒的表现形式是一样的,只是可以理解为它们所处的上下文环境不一样,新的BFC和根元素的BFC各自形成独立空间,接下来要说的就是在处理浮动时为什么表现为display:blockdisplay:table的区别,前者不会形成新的BFC,而后者则会。

清楚浮动和包含浮动的区别

display: block

在没有形成新的BFC时,如果块级父元素heightauto或不设置,没有设置paddingborder属性,且只有块级子元素,那么父元素的默认高度将由子元素的盒模型决定,从最高块级子元素的外边框边界到最低块级子元素的外边框边界之间的距离(注意不包含子元素的的外边距margin值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style type="text/css">
.father {
width: 100%;
background: #ececdd;
}

.son {
height: 100px;
width: 100%;
margin-top: 50px;
margin-bottom: 30px;
background: #d3cff7;
}
</style>

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

如果fahter块有内容或者有paddingborder属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<style type="text/css">
.father {
width: 100%;
background: #ececdd;
border: 1px solid #f9d7f7;
}

.son {
...
}
</style>

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

可以看出上面两个例子的不同之处在于第一个例子father块的大小和son块一样,那么如果两个一样的此father块之间的外边距如何计算呢?

  • father块带border属性
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
<style type="text/css">
.father {
width: 100%;
background: #ececdd;
border: 1px solid #f9d7f7;
}

.son {
height: 100px;
width: 100%;
margin-top: 50px;
margin-bottom: 50px;
background: #d3cff7;
}
</style>

<div class="father">
<div class="son">
son
</div>
</div>
<div class="father">
<div class="son">
son
</div>
</div>
son
son
  • father块不带border属性
son
son

可以发现两个son块在外边距上发生了重叠,使得外边距为较大值50px;如果此时给第二个father块一个margin-top: 40px;的属性值,外边距重叠仍然使两个son块的距离为50px,因此father块一个margin-top属性值就不能起到效果了。

son
son

display: table

如果使father块触发BFC上下文呢?

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
<style type="text/css">
.father {
width: 100%;
background: #ececdd;
display: table; /*触发新的BFC上下文*/
}

.son {
height: 100px;
width: 100%;
margin-top: 50px;
margin-bottom: 50px;
background: #d3cff7;
}
</style>

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

可以发现两个son块之间的距离为70px,并且父元素的高度还包含了son块的margin值,如果此时同样给第二个父元素一个margin-top值,可以发现此时的40px有了效果

son
son

当然如果把son块换成float元素(需要注意的是son块又形成了新的BFC),那么效果仍然一样,而且不会产生高度塌陷,因为BFC会计算浮动元素的高度,而不会使father块高度塌陷,也就起到了包含浮动的作用

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
<style type="text/css">
.father {
width: 100%;
background: #ececdd;
display: table; /*触发新的BFC上下文*/
}

.son {
height: 100px;
width: 100%;
margin-top: 50px;
margin-bottom: 50px;
background: #d3cff7;
float: left;
}
</style>

<div class="father">
<div class="son">
son
</div>
</div>
<div class="father">
<div class="son">
son
</div>
</div>
float son
float son

所以最后你可以比较一下以下两种的情况的区别了

  • 去除浮动
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
<style type="text/css">
.father {
width: 100%;
background: #ececdd;
display: block;
}

.father: after {
content: "";
clear: both;
}

.son {
height: 100px;
width: 100%;
margin-top: 50px;
margin-bottom: 30px;
background: #d3cff7;
}
</style>

<div class="father">
<div class="son">
son
</div>
</div>
<div class="father" style="margin-top: 40px;">
<div class="son">
son
</div>
</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
<style type="text/css">
.father {
width: 100%;
background: #ececdd;
display: table;
}

.father: after {
content: "";
clear: both;
}

.son {
height: 100px;
width: 100%;
margin-top: 50px;
margin-bottom: 30px;
background: #d3cff7;
}
</style>

<div class="father">
<div class="son">
son
</div>
</div>
<div class="father" style="margin-top: 40px;">
<div class="son">
son
</div>
</div>

那么你就会清楚为什么bootstrap清除浮动的less是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14

// The use of `table` rather than `block` is only necessary if using
// `:before` to contain the top-margins of child elements.

.clearfix() {
&:before,
&:after {
content: " ";
display: table;
}
&:after {
clear: both;
}
}

参考文献

# BFC, css, float

评论

Your browser is out-of-date!

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

×