浮动的出现以及清除方式
一、为什么出现浮动?
我们先说说CSS中的定位机制:普通流、浮动、绝对定位(其中"position:fixed"是"position:absolute"的一个子类)。
1、普通流: normal flow,或者称之为常规流。除非专门制定,否则所有框都在普通流中定位。普通流中元素框的位置有元素在HTML中的位置决定。块级元素从上到下依次排列,框之间的垂直距离由框的垂直margin计算得到。行内元素在一行中水平布置。position:relative和position:static都会出现在正常的流中;position:absolute、position:fixed、float:left | right;都会脱离文档流。
position:static; 默认值,没有定位,元素框正常生成,出现在正常的流中,会忽略top、bottom、left、right和z-index声明。
2、绝对定位: position:absolute; 相对于已定位的最近的祖先元素(可以是相对定位的祖先元素),如果没有已定位的最近的祖先元素,那么它的位置就相对于最初的包含块(如body)。绝对定位的框可以从它的包含块向上、右、下、左移动(left、top、right、bottom)。绝对定位的框脱离普通流,所以它可以覆盖页面上的其他元素,可以通过设置Z-index属性控制这些框的堆放次序。
固定定位:position:fixed; 相对于浏览器窗口进行定位,其余的特点类似于绝对定位。元素位置通过left、top、right、bottom进行规定。
3、浮动: 浮动的框可以左右移动,直至它的外边缘遇到包含框或者另一个浮动框的边缘。如果包含块太窄,无法容纳水平排列的浮动元素,那么其他浮动块向下移动,直到有足够多的空间。如果浮动元素的高度不同,那么当它们向下移动时可能会被其他浮动元素卡住。浮动框不属于文档中的普通流,当一个元素浮动之后,不会影响到块级框的布局而只会影响内联框(通常是文本)的排列,文档中的普通流就会表现得和浮动框不存在一样。
当浮动框高度超出包含框的时候,也就会出现包含框不会自动伸高来闭合浮动元素(“高度塌陷”现象)。浮动元素,就是漂浮于普通流之上,像浮云一样,但是只能左右活动。正是因为浮动的这种特性,导致本属于普通流中的元素浮动之后,包含框内部由于不存在其他普通流元素了,也就表现为高度为0(高度塌陷)。在实际布局中,往往这并不是我们所希望的,所以需要闭合浮动元素,使2其包含框表现出正常的高度。
具体分析可见:https://www.cnblogs.com/iceflorence/p/5798553.html
举例:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
.container{
width:350px;
border:2px solid red;
}
#div1 {
float:left;
width: 100px;
height: 100px;
background: yellow;
}
#div2 {
float:left;
width: 100px;
height: 100px;
background: red;
}
#div3 {
float:left;
width: 100px;
height: 100px;
background: blue;
}
</style>
</head>
<body>
<div class="container">
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
</div>
</body>
</html>
结果如左图所示,但是我们希望得到右图的结果:
二、清除浮动与闭合浮动的区别
清除浮动: 对被影响文档位置的块级元素(不是浮动元素)添加属性 clear: left | right |both |none; ,使其下移,直到元素两边没有浮动元素。
闭合浮动: 更确切的含义是使浮动元素闭合,使父元素高度不再塌陷,高度被撑开了,使父元素能够包围浮动元素,从而减少浮动带来的影响。
举例:
出现浮动元素未进行处理:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
* { margin:0; padding:0; }
h2 { font-size:16px; margin:20px 0; text-align:center; color:#555; }
.left { float:left; }
.right { float:right; }
.left, .right { *display:inline;}
div { padding:15px 20px; font-size:14px; color:#333; }
.content { padding-bottom:200px; }
.warp { border:1px solid blue; width:600px; margin:30px auto 5px; background:#F5F5F5; }
.main { height:60px; width:50%; background:#FFE3D7; }
.side { width:20%; background:lightblue; }
.footer { border:1px solid #CCC; background:#EEE; width:600px; margin:5px auto 30px; }
</style>
</head>
<body>
<div class="content">
<h2>出现浮动未处理</h2>
<div class="warp">
<div class="main left">.main:浮动了(float:left),现代浏览器中我没能把父元素warp撑高</div>
<div class="side left">.side:浮动了(float:left)</div>
</div>
<div class="footer">.footer</div>
</div>
</body>
</html>
表现结果:
下面看看分别添加清除浮动和闭合浮动的效果:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
* { margin:0; padding:0; }
h2 { font-size:16px; margin:20px 0; text-align:center; color:#555; }
.clearfix:after { clear:both; content:"."; display:block; height:0; visibility:hidden; }
.clearfix { *zoom:1;}
.clear { clear:both; }
.left { float:left; }
.right { float:right; }
.left, .right { *display:inline;}
div { padding:15px 20px; font-size:14px; color:#333; }
.content { padding-bottom:200px; }
.warp { border:1px solid blue; width:600px; margin:30px auto 5px; background:#F5F5F5; }
.main { height:60px; width:50%; background:#FFE3D7; }
.side { width:20%; background:lightblue; }
.footer { border:1px solid #CCC; background:#EEE; width:600px; margin:5px auto 30px; }
</style>
</head>
<body>
<div class="content">
<h2>闭合浮动 与 清除浮动 的区别</h2>
<div class="warp">
<div class="main left">.main:浮动了(float:left),现代浏览器中我没能把父元素warp撑高</div>
<div class="side left">.side:浮动了(float:left)</div>
</div>
<div class="footer clear">.footer:我通过设置 clear:both <strong>清除浮动</strong>,虽然位置正确了,但是 warp 的高度没变</div>
<div class="warp clearfix" id="floa7">
<div class="main left">.main:warp自己闭合浮动了,所以footer不用再清除浮动了(float:left)</div>
<div class="side left">.side:我也浮动了(float:left)</div>
</div>
<div class="footer">.footer: warp通过 .clearfix 已经<strong>闭合浮动</strong>了,我不需要做什么</div>
</div>
</body>
</html>
表现结果:
通过上述实例发现,我们要达到想要的结果,更确切地说是闭合浮动,而不是单纯的清除浮动。
三、闭合浮动的方法
1、添加额外的标签
通过在浮动元素末尾添加一个空标签,其他块级元素标签等亦可。
<div class="warp" id="float1">
<h3>1)添加额外标签</h3>
<div class="main left">.main{float:left;}</div>
<div class="side left">.side{float:right;}</div>
<div style="clear:both;"></div>
</div>
<div class="footer">.footer</div>
原理:添加一个空div,利用css定义的clear: both; 清除浮动,让父级div能自动获取到高度。
优点:简单,代码少,浏览器支持好,不容易出现问题。
缺点:如果页面浮动布局多,就要增加很多空div,代码语义化变差,有违结构与表现的分离,不建议使用。
2、使用br标签和其自身的html属性
<div class="warp" id="float2">
<h3>2)使用 br标签和其自身的 html属性</h3>
<div class="main left">.main{float:left;}</div>
<div class="side left">.side{float:right;}</div>
<br clear="all" />
</div>
<div class="footer">.footer</div>
这个方法有些小众,br有clear="all | left |right |none ";
优点:比空标签方式语义稍强,代码量少。
缺点:同样有违结构与表现的分离,不推荐使用。
3、使用 :after 伪元素
.clearfix:after { clear:both; content:""; display:block; height:0; visibility:hidden; }
原理:对父元素div的class类添加伪元素,伪元素中含有属性:clear:both; content:"";,after伪元素其实也是通过 content 在元素的后面生成块级元素;父元素会自动取得高度。
优点:结构和语义化完全正确,代码量适中。浏览器支持好,不容易出现问题。建议定义公共类,以减少css代码。
提示:IE8以上和非IE浏览器才支持:after,zoom可解决ie6、ie7浮动问题。使用如下:
.clearfix:after { clear:both; content:""; display:block; height:0; visibility:hidden; }
.clearfix { *zoom:1;}
说明:zoom属性是IE浏览器的专有属性,它可以设置或检索对象的缩放比例。在平常的css编写过程中,zoom:1能够比较神奇地解决ie下比较奇葩的bug: 譬如:外边距(margin)的重叠、浮动的清除、触发ie的haslayout属性等。haslayout是ie特有的一个属性,当一个元素的haslayout属性值为true时,它负责对自己和可能的子孙元素进行尺寸计算和定位。haslayout对内联元素也有效果,当内联元素的haslayout为true的时候,可以给这个内联元素设定宽高。
原因:当设置了zoom的值之后,所设置的元素就会扩大或者缩小,高度宽度就会重新计算了,这里一旦改变zoom值时,其实也会发生重新渲染,运用这个原理,也就解决了ie下子元素浮动时父元素不随着自动扩大的问题。补充一点:*zoom:1; 前面有*,*放在css属性前面,表示这个属性仅仅应用到Internet Explorer 7以及以下版本。
4、父元素设置overflow:hidden / auto
<div class="warp" id="float3" style="overflow:hidden;">
<h3>3)父元素设置 overflow:hidden</h3>
<div class="main left">.main{float:left;}</div>
<div class="side left">.side{float:right;}</div>
</div>
<div class="footer">.footer</div>
优点:不存在结构和语义化问题,代码量极少。
overflow:hidden 缺点:内容增多的时候容易造成不会自动换行导致的内容被隐藏掉,无法显示要溢出的元素;
overflow:auto 缺点:多个嵌套后,Firefox某些情况会造成内容全选;IE中mouseover造成宽度改变时会出现最外层模块有滚动条等。
5、父元素也设置浮动
优点:不存在结构和语义化问题,代码量极少。
缺点:使得与父元素相邻的元素的布局会受到影响,不可能一直浮动到body,不推荐使用。
6、父元素设置display:table | table-cell | inline-block | flow-root
<div class="warp" id="float6" style="display:table;">
<h3>6)父元素设置display:table</h3>
<div class="main left">.main{float:left;}</div>
<div class="side left">.side{float:right;}</div>
</div>
<div class="footer">.footer</div>
优点:结构语义化完全正确,代码量少。
缺点:盒模型属性已经改变,由此造成的一系列问题,得不偿失,不推荐使用。
其中,flow-root是css3新属性,元素生成块容器框,并使用流布局将其内容列出来。它总是为其内容建立新的块级格式化上下文。可以闭合浮动,但是实现条件苛刻,且兼容性差,所以使用较少。参考链接
四、总结
1、以上列举的闭合浮动的方法,无非有两类:
其一、通过在浮动元素的末尾添加一个空元素,设置clear:both; 属性,after伪元素也是这个原理。
其二、通过设置父元素overflow或者display:table;属性来闭合浮动。
关于其二的原理,涉及一个概念,BFC(块级格式化上下文,Block formatting contexts)。
2、BFC触发条件:
- float除了none以外的值;
- overflow除了visible以外的值(hidden,auto,scroll);
- display(table-cell,table-caption,inline-block);
- position(absolute,fixed);
- fieldset元素。
3、BFC的特性:
1)、块级格式化上下文会阻止外边距叠加
当两个相邻的块框在同一个块级格式化上下文中时,他们之间的垂直方向的外边距会发生叠加。如果这两个相邻的块框不在同一个块级格式上下文,那么它们的外边距就不会叠加。
2)、块级格式化上下文不会重叠浮动元素
根据规定,一个块级格式化上下文的边框不能和它里面的元素的外边距重叠。这就意味着浏览器将会给块级格式化上下文创建隐式的外边距来阻止它和浮动元素的外边距叠加。
3)、块级格式化上下文通常可以包含浮动
创建了BFC的元素就是一个独立的盒子,里面的子元素不会在布局上影响外面的元素。反之亦然,同时BFC依然属于文档中的普通流。
overflow:hidden或者auto可以闭合浮动,真是因为父元素创建了新的BFC。现代浏览器都是有BFC的,从表现上来说,hasLayout可以等同于BFC。IE6-7使用布局的概念来控制元素的尺寸和定位,那些拥有布局(hasLayout为true)的元素负责本身及其子元素的尺寸设置和定位。
4、终极方案
上面所有方案总结:使用after伪元素闭合浮动无疑是相对比较好的解决方案,即:
.clearfix:after {content:"."; display:block; height:0; visibility:hidden; clear:both; }
.clearfix { *zoom:1; }
1) display:block 使生成的元素以块级元素显示,占满剩余空间;
2) height:0 避免生成内容破坏原有布局的高度。
3) visibility:hidden 使生成的内容不可见,并允许可能被生成内容盖住的内容可以进行点击和交互;
4)通过 content:"."生成内容作为最后一个元素,至于content里面是点还是其他都是可以的,例如oocss里面就有经典的 content:"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",有些版本可能content 里面内容为空,是不推荐这样做的,firefox直到7.0 content:”" 仍然会产生额外的空隙;
5)zoom:1 触发IE hasLayout。
通过分析发现,除了clear:both用来闭合浮动的,其他代码无非都是为了隐藏掉content生成的内容,这也就是其他版本的闭合浮动为什么会有font-size:0,line-height:0。