原文地址:http://www.zhangxinxu.com/wordpress/2010/01/css-float%E6%B5%AE%E5%8A%A8%E7%9A%84%E6%B7%B1%E5%85%A5%E7%A0%94%E7%A9%B6%E3%80%81%E8%AF%A6%E8%A7%A3%E5%8F%8A%E6%8B%93%E5%B1%95%E4%BA%8C/
作者:张鑫旭
五、浮动的非本职工作
浮动的本职工作是让匿名inline boxes性质的文字环绕图片显示,而其他所有用浮动实现的效果都不是浮动应该做的事情,我称之为“非本职工作”。
或许我们并没有过多的深思,把一些实际上不是浮动该干的事情当作“这必须用浮动来实现”。举个常见的例子,列表显示,见下面的图,截自淘宝新版首页:
我不看代码就知道是用浮动实现的,我用firebug一看,果然是,不仅浮动,而且定宽。ps:要是在几个月以前,我会觉得这实在有待改进,不过现在我的心态宽了,布局思想不同而已,没有孰对孰错之分。
我可以确信,浮动这个属性诞生的那天压根没有想到自己会要做这样的事情,本来它以为自己就让文字环绕显示就OK了,功德圆满了,结果,在web2.0的时代,其却在页面布局中被滥用。可能有人会反驳,你何处此言,有何证据?
我们只要静下心来好好想想浮动的本质,实现的原理,就可以知道为什么浮动本不应该用来对页面进行布局。还记得上一部分所说的浮动的本质吗?即“包裹与破坏”。我们可以用这个(“包裹与破坏”)解释为什么浮动可以让li这类block水平的元素水平排列。
单个无浮动的li元素
看下面的HTML代码:
<ul style="width:440px;"> <li style="border:4px solid #ff6633; background:#ffffc0;"><img src="../image/border.png" /></li> </ul>
结果如下图(截自Firefox浏览器,无其他样式干扰,下同):
这里的li元素为什么会有高度?如果您认真读过前半部分内容关于line boxes模型与高度的关系的内容,应该知道,由于图片没有应用float属性,其本身有一个正常的inline box,这个inline box高度等于图片的height,在这行元素中,图片这个inline box的高度最高,于是传递给了line box,line box是个真正意义上的高度,直接作用于containing box(就是这里的li元素,使li元素有一个高度)。理解了这个您就会明白为什么要是这里的图片添加了float属性,li高度会塌陷了:浮动破坏了inline box。这个后面会详细讲解。
这里的li没有添加float属性,可以见到li宽度100%自适应于父ul标签。一切显得那么的和谐!
单个左浮动的li元素
看下面的HTML代码:
<ul style="width:440px;"> <li style="border:4px solid #ff6633; background:#ffffc0; float:left;"><img src="../image/border.png" /></li> </ul>
结果如下图:
相比上面而言,这里多了个float:left;,浮动的“包裹性”一目了然:水平方向上,li宽度紧贴内部元素。我在前文曾说过这么句结论性的话:“撇开浮动的‘破坏性’,浮动就是个带有方位的display:inline-block属性”。这不难理解,您可以讲这里的float:left;改成display:inline-block;最后实现的效果基本上就是一样的。display:inline-block将对象呈递为内联对象,但是对象的内容作为块对象呈递。所谓对象呈递为内联对象就是元素呈递为inline box,所以浮动“包裹性”所产生的结果就是使得元素转为了line box模型中的inline box元素。
浮动的“包裹性”让元素变成类似于inline box的元素,而浮动的“破坏性”正是破坏inline box元素的,这其中岂不有矛盾。其实非也,对于block水平的这类块状元素需要先让其变成类似效果的内联元素,然后再破坏之,实乃先诱拐再奸杀的生动实例啊!
又是我反复提到的,浮动破坏了inline box,也就是破坏了高度,所以这里含有浮动属性的li元素实际上是没有高度的。所以呢,要是后面还有同样的li标签的话,就会水平对齐排列的。如下:
多个左浮动的li元素
看下面的HTML代码:
<ul style="width:440px;"> <li style="border:4px solid #ff6633; background:#ffffc0; float:left;"><img src="../image/border.png" /></li> <li style="border:4px solid #ff6633; background:#ffffc0; float:left;"><img src="../image/border.png" /></li> </ul>
结果如下图:
左浮动的li元素和无浮动的li元素
看下面的HTML代码:
<ul style="width:440px;"> <li style="border:4px solid #ff6633; background:#ffffc0; float:left;"><img src="../image/border.png" /></li> <li style="border:4px solid #ff6633; background:#ffffc0;"><img src="../image/border.png" /></li> </ul>
结果如下图:
后面一个li无float属性,直接无视前面的float li元素,宽度100%显示,而且与float属性的li同一水平线排列,为何?因为浮动破坏了li的实际高度(原因我讲得太多次了),只是图片是个实体,无法与同样实体的同一文档流的图片重合,所以图片靠在一起。
六、证据
1. 首要证据
前文是着重分析了浮动的“本职工作”:文字环绕显示;本文着重分析了浮动的“非本职工作”:列表布局;两者都是用的同样的原理解释的,那么有什么证据可以证明“页面布局”不是浮动的“本职工作”呢。答案关键字就是:高度塌陷。
我在多个地方浓墨重彩的讲浮动与高度的关系,浮动使高度塌陷的原因,自己都觉得唠叨的太多了,这里不再讲。直接讲些实际的东西。
您是否发现,浮动布局会让父标签高度缺失,但是实现文字环绕图片效果的时候父标签无需清除浮动。还是上面的例子,我们看看多li元素浮动时ul标签高度在哪里,我们可以给ul加个边框属性做测试,测试代码如下:
<ul style="width:440px; border-top:4px solid #a0b3d6; border-bottom:4px solid #34538b;"> <li style="border:4px solid #ff6633; background:#ffffc0; float:left;"><img src="../image/border.png" /></li> <li style="border:4px solid #ff6633; background:#ffffc0; float:left;"><img src="../image/border.png" /></li> </ul>
结果如下图:
上图显示ul高度为0,这是必然的,正常的,应该的。您要是在IE下看到ul有高度,认为这是Firefox等浏览器有问题就错了,您把ul的width属性去掉,看看,会发现IE下ul高度也为0, 这是IE的layout作祟。至于为什么ul高度为0,我前面很多地方都已经讲了,不再说了。但是,从中我们可以看到,浮动本不是用来列表布局的,而是用来使元素环绕显示的,因为元素环绕(例如文字),其自身是含有inline boxes高度的,这是inline水平的元素形成高度的基础,所以,虽然浮动元素没有高度,但是其周围环绕的元素是有高度的,只要环绕元素比浮动元素高度高,父标签无高度的问题自然也就没有了,但是纯粹一堆浮动元素会有高度吗?没有。所以浮动元素塌陷的问题根本就不是浏览器的bug,而是我们没有正确地深入地了解浮动,是我们自己使用不当,因为浮动本不应该用在这里的。
2. 其他证据
为什么说浮动的本职工作不是页面布局而应该是是元素环绕呢。还有其他些证据。
历史
CSS刚诞生的时候,那时候的网页还是表格布局的时代,能显示几张图片就不错了,复杂的列表显示,布局啊这些概念还是很淡薄的,浮动出现的意义就是让文字能够环绕图片显示而已,就如word般。
现象
Google是目前互联网领域的老大,技术很牛叉,毋庸置疑,但是,当我发现它所有的页面几乎都是表格布局的时候,很诧异;还有twitter,也是表格布局的;
为什么这些顶尖的web网站使用表格布局,而不跟随潮流呢,起初我是百思不得其解。现在,我是明白了,这么做是有他的道理的,难道要用一堆浮动的元素去布局吗?这不是浮动该干的事情。而且,浮动是个很不好惹的魔鬼。
七、浮动是个魔鬼、混球
浮动的存在就是个破坏。浮动效果的实现就是先破坏自身(挥刀自宫),然后影响别人。页面中只要出现一个浮动,势必有其他元素受其影响。这是开发和维护中的一个定时炸蛋(敏感词)。我们想想浮动的意义,只是让文字环绕图片显示而已,所以其必须破坏自身模型和高度。但是,不知道幸运还是不幸,浮动的这种置之死地而后生的特性居然可以让元素布局,加上其表面意思很好理解,兼容性还不错,使用方便,于是在舍弃了table布局的这个web时代里,浮动成为当前页面布局的主流。想想,真是一件可怕的事情。
有些人可能没有意识到浮动其实是个很糟糕的东西,是CSS中的一个魔鬼。为什么,或许因为用浮动布局页面发现还行,即使偶尔出现些小bug,打打补丁也可以解决。或许是因为没有体会到非浮动元素和浮动元素在开发和维护上所消耗的成本精力的差别。有些人可能知道浮动这个属性能不用就不用,但是由于找不到更好的替代方法,所以还是有很多地方使用了浮动布局,例如选项卡,列表显示等。
理论上而言,除了浮动的环绕效果之外,我们使用浮动做的其他工作都可以使用别的CSS属性实现,而且大部分情况是比浮动的效果好:首先没有了清除浮动一说,二是有关浮动的一堆bug也不会出现,三是元素间独立了,不会像浮动一样改变一个会影响其他。但是,考虑到大多数人对CSS掌握的程度,以及浏览器的一些兼容性问题等,很多时候,我们不得不使用看上去使用方便但潜在问题较大的浮动属性。所以,有关浮动的一些特性的研究还是很有必要的,这就是下面的重点。
八、解决高度塌陷的问题 �C 清除浮动
CSS中有个讨论较多的话题就是如何清除浮动,清除浮动其实就一个目的,就是解决高度塌陷的问题。为什么会高度塌陷?什么时候会高度塌陷?塌陷原因是:元素含有浮动属性 �C 破坏inline box �C 破坏line box高度 �C 没有高度 �C 塌陷。什么时候会塌陷:当标签里面的元素只要样子没有实际高度时会塌陷。所以呢,并不是只要有浮动元素就会坍塌,就要清除的,CSS水平高低衡量的标准之一就是该用什么样式就用什么样式,不多用也不少用。
下面就来讲讲如何清除浮动,zxx:写到这儿,一下子轻松了。
IE下清除浮动准则很简单,使元素haslayout就可以了。如宽度值,高度值,绝对定位,zoom,浮动本身都可以让元素haslayout。显然,首选zoom:1;不会干扰任何样式。非IE浏览器常用的是overflow属性,overflow:hidden;或是overflow:scroll都可以,不过由于后者经常一不小心出现滚动条,所以前者用的更多些。由于现代浏览器都支持after伪类伪元素,所以常常也会用after写入一个clear属性的元素清除浮动。当然,最投机取巧的方法就是直接一个<div style="clear:both;"></div>放到当作最后一个子标签放到父标签那儿。下面小结这几个方法。
投机取巧法
就是直接一个<div style="clear:both;"></div>放到当作最后一个子标签放到父标签那儿,此方法屡试不爽,兼容性强,使用方便,是初学时使用的上佳之选。但是我从来不用,因为我看到的是个巨大的浪费,浪费了一个标签,而且只能使用一次,我个人是无法容忍的,所以这个方法不推荐。而且有时候一不留神中间多了个空格会产生一段空白高度的。
2. overflow + zoom方法
.fix{overflow:hidden; zoom:1;}
此方法优点在于代码简洁,涵盖所有浏览器,可谓不错的选择啊。不过也是有问题的,就是这个overflow:hidden;是个小炸弹,要是里面的元素要是想来个margin负值定位或是负的绝对定位,岂不是直接被裁掉了,所以此方法是有不小的局限性的。我一般不用这个方法,只是有时候顺便去除浮动时用用。
3. after + zoom方法
先来简单讲讲after,所谓after,就是指标签的最后一个子元素的后面。于是呢,我们可以用CSS代码生成一个具有clear属性的元素,其中的关键样式就是content了。或许您从网上看到的content里面的内容是”.”一个点,我试了很多次,貌似随便写什么东西都没有问题,比如content:'clear both';没问题,或是content:'张鑫旭'也是ok的。于是有:
.fix{zoom:1;} .fix:after{display:block; content:'clear'; clear:both; line-height:0; visibility:hidden;}
这里的line-height:0写成height:0也是可以的。此方法可以说是综合起来最好的方法了,我都是用这个样式清除浮动的,不会影响任何其他样式,通用性强,覆盖面广,我很推荐哦。
九、float与JavaScript
JavaScript可以改变CSS的属性,其他些属性还好,但是这个float值得一说,为何呢,因为float貌似是JavaScript中的一个关键字,不能使用obj.style.float="left";这样的句子。得使用其他写法。
IE浏览器:
obj.style.styleFloat = "left";
其他浏览器:
obj.style.cssFloat = "left";
十、最后的总结
其实原本是想通过深入分析浮动的前世今生,本质意义来揭示其丑陋的真面目的,可能是在文字表达上还欠火候,总觉得表达的有些凌乱,估计很轻松就知道我在说什么的人较少,本来是篇很有技术含量的文章,但是在表现上做的不够好,可能是内容太多,自己想要表达的内容太多,于是反而有些混乱。所以,最后其实很多东西没有继续详细展开讲解,例如清除浮动的本质是什么?浮动产生一系列bug的根本原因是什么?浮动布局更好的替代方案是什么?因为内容已经够多够乱的了,所以我决定这些内容留到以后,思路清晰的时候再表述出来。
本文可以说是有别于目前绝大多数关于浮动的文章,我没有讲浮动的使用实例啊之类的东西,也没有去分析浮动的一些特性,仅仅从最根本的地方分析浮动,我想这个比单纯的讲解几个浮动的应用作用更深远。而且还有一点,本文其实某种程度上是批判浮动的,希望开发者尽可能的少使用浮动,少使用浮动去做些其实并不是浮动应该做的事情。所以要是花功夫讲浮动的应用的话就是搬石头砸自己的脚。
本文很多理论的东西,多是自己的个人见解,所以实际上有些认识可能是不准确的,欢迎有不同观点的同行们反驳,也欢迎交流(通过评论或是去这里进行提问交流)。