05 September 2013
关于clearfix清除浮动等导致父容器高度塌陷的细节
因为项目常常要求快速开发,Bootstrap前端框架用的比较多,里面写好了很多class,可以直接拿来用,很方便。
而其中有一个类:.clearfix
,是使用最多的类之一,它可以用来清除由于内部元素float导致的父容器高度塌陷问题。
我们先来看看它的写法:
.clearfix {
*zoom: 1;
}
.clearfix:before,
.clearfix:after {
display: table;
line-height: 0;
content: "";
}
.clearfix:after {
clear: both;
}
这种写法,常叫做“micro clearfix hack”,效果我们试试都知道,并没有问题,但是对这其中的原理我想很多人可能比较迷惑,比如说为毛要用 display:table
。
参阅Nicolas Gallagher的一篇文章《A new micro clearfix hack》,文章中解释了为什么使用 :before
,为什么使用 display:table
,等等。
别小看这个 .clearfix
,看起来就这么几行,涉及的东西还真不少。
我同时打开了Chrome,FF,Opera、三个虚拟机分别跑IE6、IE7、IE8 ,在这6个浏览器下同时反复测试后,终于搞清楚了每一行的意思。
首先,html如下:
<style>
.container {
background: yellow;
margin: 40px 0px;
}
.left {
width: 30px;
height: 100px;
background: blue;
float: left;
margin: 50px 0px;
}
</style>
<div class="wrapper">
<div class="container clearfix">
<div class="left ">
abababasdfjlsjdflkjlsk
</div>
</div>
</div>
1.关于使用display:table还是display:block;
首先,原文中的意思,用 display:table
的原因是为了避免外边距margin重叠导致的margin塌陷。
好吧,我测来测去,都看不出所谓的margin塌陷在哪啊…
最后又查了下资料 ,发现所谓的margin塌陷是发生在普通流上的,而且要满足几个条件(汗…基础又不扎实了):
- 两个或多个元素之间的margin相互作用;
- 毗邻,指两元素之间没有被非空元素分隔开(如padding);
- 垂直方向,只有margin-top,margin-bottom会相互作用;
- 普通流(既文档流)。float不是普通流,position:absolute也不是普通流。
好吧,我的测试元素 float:left
,当然不会塌陷啦。
ok,将里层元素的 float:left
去除,将 .clearfix
中的display改为block,好吧,看到塌陷了。
这里还要注意!这个情况每个浏览器的表现还不一样。
Chrome,Opera和FF,IE9,IE10等较新的浏览其中,使用 display:block
时,上下的margin都会被重叠部分吃掉。
而IE8表现为只有上margin被吃掉,而IE6、7因为不支持伪元素,所以改变display对其并无影响。
好了,明白为什么用 display:table
后,让我们来看看到底 display:table
是何方神圣。
display:table的作用是,让元素展示成像一个table。
在这里它的作用是在两个伪元素中分别自动生成一个匿名表达单元(anonymous table-cell),并将这一行展示为一个块级元素。
ok,回头看margin塌陷的第二个条件,两元素间需要没有被非空元素分隔。
在这里,由于display:table,在上下伪元素中生成了匿名table-cell,它分隔了两个元素(抽象上),成功的阻止了margin塌陷。(个人理解)
2.IE下的一些问题
首先当然要说的是IE6、7两个老古董,由于它们不认识伪元素是什么东东,所以上面一大串都与它俩无关。
影响它们的是那一行 hack: *zoom:1;
这里又要扯到另外一个概念layout了,详细可以参阅:关于ie的layout深层剖析。
简而言之,就是layout是ie下的专有属性,它由每个元素内部的hasLayout属性决定,一个元素是否具有layout可能会引起一系列问题,甚至影响它们的子元素,可能引起的问题有:
- IE很多常见的浮动 bug 。
- 元素本身对一些基本属性的异常处理问题。
- 容器和其子孙之间的边距重叠(margin collapsing)问题。
- 使用列表时遇到的诸多问题。
- 背景图像的定位偏差问题。
- 使用脚本时遇到的浏览器之间处理不一致的问题。
ok,不扯远。看到1,3条,binggo!正是我们clearfix要解决的问题,那么怎么才能使元素具有layout呢?
很简单,zoom:1
即可。
说到这里,另外的问题又来了。
当元素不是浮动元素时,显示没有问题,上下margin都正常,但是一旦元素 float:left
, “垮嗒”一声,margin-bottom塌了!
这又是什么情况?
各种查之后得知,这是ie6、7下的一个bug:当元素float以后,margin-bottom会失效。
而css解决的办法竟然是…木有!
唯一让它显示margin-bottom的办法就是在float元素之后添加一个空元素,并设置clear:both;
或者直接不实用margin-bottom,而改用父元素的padding-bottom。
或者你可以将 *zoom:1
这句改为
.clearfix {
*zoom: expression(this.runtimeStyle.zoom="1",this.appendChild(document.createElement("br")).style.cssText="clear:both;font:0/0 serif");
}
但是考虑到css表达式效率低的问题,这个办法谨慎使用。
3.最后…在测试时发现的一个小问题。
就是我的测试html中的子元素是原本定高定宽的。
在所有浏览器中表现出来的都是一个正方形,文字超出子元素,唯有IE6,它会随着子元素中的字符长度而边长!
这一点很匪夷所思。。设置 overflow:hidden
后消失。
唉,今天先不研究这个问题了,记住有这么一个奇怪现象就好了。
Posted in 2013-09-05 18:49