本文主要讲述了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; } .inner-float { width: 100%; height: 20px; background: #d3cff7; float: left; margin-bottom: 40px; 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
浮动,p
和div1
布局重叠,为了解决这个问题,可以使p
元素左侧不允许有浮动元素,给p
元素添加一个clear:left
的样式之后
1
2
3333333333333333333333333333333333333333333
可以发现
p
的位置下移从而使
p
和
div1
布局不会重叠,其实是使用清除之后,自动增加了
p
元素的上外边距,确保它落在浮动元素
div1
的下面。
CSS2.1引入了
清除区域的概念,
清除区域是在元素上外边距之上增加的额外间隔(确保浮动元素不会与该元素重叠),不允许浮动元素进入这个范围,意味着设置
clear
属性的
p
元素的外边距并不改变,之所以该元素会向下移动是因为
清除区域造成的。
div1
和
div2
的高度分别是
100px
和
50px
, 因此
清除区域的高度在
50px
左右(不算
border
和浏览器代理初始样式等)。如果给
p
元素一个上外边距
margin-top:30px
,则
p
元素并不会向下移动
30px
,而是在解析完样式之后
清除区域的高度变成了
50px - 30px
左右,而
p
元素仍然位置不变,如果要使
p
元素下移,则需要使
p
元素的
margin-top
值高于最大的
清除区域高度
50px
,并且向下位移的距离也是
margin-top
值减去最大的
清除区域高度。
通常情况下,我们当然很难准确计算最大的
清除区域高度,因此要使浮动元素
div1
和
p
元素有一个确定的值很难通过设置
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 box
是display
属性为block, list-item, table
的元素,会生成block-level box
,并且参与block fomatting context
。 具体可以从什么是BFC中了解BFC
的特性。
BFC
特性
BFC
会阻止垂直外边距(margin-top
、margin-bottom
)折叠(属于同一个BFC
的两个相邻Box
的margin
会发生重叠 )
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
折叠了。所以上下边距30px
和50px
并不会重叠,因为不属于同一个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>
|
BFC
不会重叠浮动元素,由于第二个div
使用overflow: hidden
触发了新的BFC
,因此它被排布在float
元素的右边而不会重叠。
需要注意的是形成新BFC
上下文中的块级盒的表现形式和根元素下(根元素也也会触发BFC
)的块级盒的表现形式是一样的,只是可以理解为它们所处的上下文环境不一样,新的BFC
和根元素的BFC
各自形成独立空间,接下来要说的就是在处理浮动时为什么表现为display:block
和display:table
的区别,前者不会形成新的BFC
,而后者则会。
清楚浮动和包含浮动的区别
display: block
在没有形成新的BFC
时,如果块级父元素height
为auto
或不设置,没有设置padding
或border
属性,且只有块级子元素,那么父元素的默认高度将由子元素的盒模型决定,从最高块级子元素的外边框边界到最低块级子元素的外边框边界之间的距离(注意不包含子元素的的外边距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>
|
如果fahter
块有内容或者有padding
或border
属性
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>
|
可以看出上面两个例子的不同之处在于第一个例子father
块的大小和son
块一样,那么如果两个一样的此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
| <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
块在外边距上发生了重叠,使得外边距为较大值50px
;如果此时给第二个father
块一个margin-top: 40px;
的属性值,外边距重叠仍然使两个son块的距离为50px
,因此father
块一个margin-top
属性值就不能起到效果了。
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; }
.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
块之间的距离为70px
,并且父元素的高度还包含了son
块的margin
值,如果此时同样给第二个父元素一个margin-top
值,可以发现此时的40px
有了效果
当然如果把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; }
.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>
|
所以最后你可以比较一下以下两种的情况的区别了
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
|
.clearfix() { &:before, &:after { content: " "; display: table; } &:after { clear: both; } }
|
参考文献