代码改变世界

[译] 清除浮动的新方法

2011-11-06 22:49  AndrewCja  阅读(1777)  评论(5编辑  收藏  举报

怎样不用结构化标记而能清除浮动

(这个清除技术是由csscreator.com的Tony Aslett开发的。
这儿可以找到最早为人知的最基本的:after想法。)

请注意,从2008-3-4起,您正在阅读的这篇文章已经算是有些陈旧了。因为从这边文章被创作之日起,关于清除浮动的更多薪信息开始出现。你会觉得这里的另外一篇文章也非常有趣。

清除浮动的过时方法

当一个浮动元素被包含在一个具有可见边框和背景的盒子内时,浮动元素并不能将包含盒子的底边向下撑开。事实上,浮动元素被包含盒子忽略了,从而像一面旗子一样挂在包含盒子的底边之下。只熟悉Windows下IE的人会摇头说:“并不会那样的呀!”。是的,IE/Win确实会将浮动元素包裹在包含盒子内,但只能是在包含盒子刚好具有微软特有的haslayout的时候才这样。

IE中这种能包裹浮动元素的行为还有可能被关掉,只要你将鼠标悬停在盒子内的链接上时链接的背景或其他几个CSS属性当中的一个被改变。真是够混乱的,不过我们会在“toggle问题”一节进行深入的分析。

W3C建议放置一个清除元素在包含盒子的最后,这样的元素能影响到盒子的高度,促使包含盒子能包裹清除浮动元素上边的浮动元素。在我们的文章浮动原理中有更详细的论述:

“...如果你给下面的盒子清除属性值,{clear: both;}。这样做的结果是将边界扩展到了清除浮动的盒子上边,将此盒子往下推,直到此盒子被推到浮动元素之下。换句话说,浏览器增加了清除元素的上边界的高度,直到这个高度能使得清除元素在浮动元素的下面。”

所以,这样的清除元素和前面的浮动元素不能在同一水平线上,它应该正好在浮动元素之下。下面这幅图展示了上述情况,红边框代表包含元素:

Shows how a box may clear below a float.

让包含元素看起来能够包裹内部浮动元素的标准方法是放置一个清除元素在包含元素里的最下面,使包含盒子的下边缘处于浮动元素的下面。这样,浮动元素看起来就像被包含盒子包裹了,虽然事实上并不如此。使用清除元素的代码通常像下面这个样子:

<div> <!-- float container -->
  <div style="float:left; width:30%;"><p>Some content</p></div>
  <p>Text not inside the float</p>
<div style="clear:both;"></div>
</div>

因为代码里的具有清除设置的div是非浮动的,所以盒子必然要包裹它,同时因为这个div的上边界(浏览器为因有clear属性而添加上边界),它将包含元素的下边缘向下推至浮动元素的下边缘之下。

此方法的缺点

首先,最大的一点,此方法一点儿也不直观,需要在标记中添加一个额外的元素。CSS的最大特质之一就是帮助现代网站减少膨胀的HTML标记。所以,为了使浮动元素能够被包裹在包含元素中而使标记重新膨胀的做法并理想。

除此之外,某些浏览器在一些情况下会对某些类型的浮动元素产生问题。Mozilla对清除问题特别敏感。

在此之前还没有其他更好的方法,但现在有了!感谢Tony Aslett(csscreator.com的创建者和维护者)的努力,我们现在可以在非IE浏览器中使用CSS来“清除”包含浮动元素的容器,而让IE仍然非标准地清除自己。最终结果就是,现在我们可以避免在HTML标记中添加恼人的清除元素了。好极了!

“清除”,21世纪的新方式

在此新方法中不须要使用清除元素。IE/Win总是会包裹浮动元素(假设容器有一个声明了的维度),其不受此方法并的影响,但非IE元素需要一个清除元素的替代品。下面看看怎么做。

使用 :after

这个CSS2属性允许在一个元素里的最后添加内容。这意味着不须要手动在HTML添加标记。要添加的内容是在CSS样式表中指明的。此内容会出现在页面中,就像在目标元素内的其他元素后面插入了真实的HTML元素。这些由:after产生的内容不能接受某些CSS属性,包括positon,float,列表和表格属性。然而,clear属性是被允许的。你看到了我们将要做什么了吗?

想像我们使用:after来插入一个普通字符,比如句号。然后给产生的元素设置{clear: both;}。这就是你所要做的全部工作,但没有人想让一个空行扰乱包含盒子的结尾,所以我们同时设置{height: 0;}和{visibility: hidden;} 以让句号不显示出来。

.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}

注意到{display: block;} 也被应用到:after元素上,如果不这样的话,新增的元素默认是内联的,从而无法使clear属性生效。Tony的方法一开始使用"overflow: hidden;"来影藏句号,可惜的是,新版的浏览器会显示句号。

至于IE?

因为IE7还不支持:after伪类,我们仍然需要依靠IE6的“自动清除”功能,而这样的行为需要包含浮动的元素具有haslayout设置。一个简单的"zoom: 1;" 声明就能在IE5.5及以上版本生效,不过这个属性是IE专有的,要通过标准验证须要将其影藏

作为副效应,包含浮动元素的容器上的hasloyout还能避免其他几个IE/Win下的bug。然而,这个容器元素须要被放置在前面的外部浮动元素之后吗?IE height fix会促发微软专有而非标准的浮动模型,所以还请提高一下警惕。

Toggle问题

在IE中,自动包裹的行为事实上有些小问题。你肯定曾经遇到过此问题,不是吗?是的,IE总是有很多bug。这个bug出现在包含元素里头有链接跟在浮动元素后头时。当这种情况出现时,如果将鼠标悬停在链接上面,自动包裹的行为将被轮转或者“关闭”,促使包含元素的底边突然跳到非浮动的被包含内容的底边。而悬停其他链接时会恢复这个行为。这个有趣的效果被理所当然地称作IE/Win Guillotine Bug。使用IE/Win阅读这篇文章的朋友可以玩玩下面的demo。若想要得到更详细的解释则请看IE/Win Guillotine Bug页面。

此toggling行为仅当a:hover被用于改变链接背景或其它样式如补白、边界或链接上的任何字体样式时发生。悬停的时候改变文本颜色并不会toggle这个bug。

下面的容器具有灰色背景和绿色边框,浮动元素具有深褐色背景和黄色边框。请注意浮动元素外头的第三和第四个链接怎样促发Guillotine Bug,而前两个则恢复它。这似乎和真实的文本行相关,所以在头两行后的链接都会促发这个问题。在浮动元素内的链接也会恢复这个问题。对于IE来讲,这并不稀奇,确实有很多bug。
Screenshot

Float Link
Any link in this float will restore the cutoff portion, as will the links in the first two text lines outside the float. Something makes those first two lines "special".
Link
Link
Link
Link
Float Link
The non-floated links to the left are wrapped in a paragraph, and that paragraph has the Holly hack value applied to it. Say "buh-bye" to the Guillotine Bug!

Link
Link
Link
Link

 

通过将链接放置在一个使用了zoom修复的p标签里头,在第二个示例中bug已经被修复。使用其他的块元素也能修复这个问题。是的,这样多用了一个元素,但跟一个清除浮动的div不同,p标签是一个“语义化”的元素。文本内容就应该被包含在语义化的容器中,联想一下,程序员也总是这样将内容包含的,也很容易将一个.clearfix类用于另一个元素。

简要说下浮动包含浮动

敏锐的读者可能已经发现,上面的示例已经包裹了浮动元素,即使在Opera 7 和 Mozilla中也如此!这是因为示例本身就是浮动的,而所有现代浏览器还有IE总是让浮动元素包裹浮动元素。当然,这样,外头的元素也是浮动的,而它有可能溜出它的容器...

将技巧组织起来

首先,需要将下面的代码添加到样式表中:

<style type="text/css">
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
</style><!-- main stylesheet ends, CC with new stylesheet below... -->
<!--[if IE]>
<style type="text/css">
.clearfix {
zoom: 1; /* triggers hasLayout */
} /* Only IE can see inside the conditional comment
and read this CSS rule. Don't ever use a normal HTML
comment inside the CC or it will close prematurely. */
</style>
<![endif]-->

在HTML中,只需要将.clearfix类加到任何包含浮动元素的容器上,再在容器里加上一个Guillotine-Bug-fixing的块元素。这样就行了,虽然这也并不完美,但总比添加一个完全额外的元素要好得多。下面来看看使用了这项修复技术的示例:

This float is not enclosed by the surrounding div container.

This container lacks the fix.

 

See how this float no longer protrudes out of the containing box, with no extra clearing element used in the container!

This float container has a class attribute of "clearfix", which applies the :after fix, or the Holly hack, depending on the browser.

IE/Mac的反击

所有这些都很美好,但不幸的是在Mac下IE并不“自动清除”浮动,同时也不支持:after,从而被清除派对拒之门外。那么,该怎么办呢?

你也许会有意放弃IE/Mac,但要考虑到使用老版本Mac的人是不能运行Safari和其他现在浏览器的。还好IE/Mac已被微软所遗弃,不久后使用它的人将几乎为零。想象一下,即使一个浮动元素跑到容器之外,其内容也不会变得晦涩难懂。只是少量的用户看不到美观的界面而已,仅此而已。然而每一个作者都必须根据他们的需求来考虑这个问题。

此页面曾经描述过如何使用JavaScript来兼容IE/Mac。不过,感谢Mark HadleyMatt Keogh,现在终于可以抛弃难看的JavaScript而直接使用CSS来修复了。好极了!

驯服IE/Mac浮动问题

要修复这个问题基本上只须要在.clearfix类中使用display: inline-block;,而在其它浏览器中影藏这个属性。就是这样!我们只须简单地修改已有的代码就能够做到。

<style type="text/css">
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.clearfix {display: inline-block;} /* for IE/Mac */
</style><!-- main stylesheet ends, CC with new stylesheet below... -->
<!--[if IE]>
<style type="text/css">
.clearfix {
zoom: 1; /* triggers hasLayout */
display: block; /* resets display for IE/Win */
} /* Only IE can see inside the conditional comment
and read this CSS rule. Don't ever use a normal HTML
comment inside the CC or it will close prematurely. */
</style>
<![endif]-->

.clearfix {display: inline-block;}会被所有浏览器识别,同时修复了IE/Mac。接着,在IE/Mac看不见的规则中,display属性被重设为block。这就是她所写的全部!简单地将上面的代码黏贴到你的CSS当中,然后在包含浮动元素的盒子上使用.clearfix。这不很酷吗?不过要警惕前面的浮动元素促发IE浮动模型,这在前面已经提到过。

感谢Alex Robinson,他发现用inline-block修复IE/Mac比用inline-table更好。

要提醒注意的地方(很重要!)

W3C浮动规范要求设置清除的元素应该排列在前面申明的浮动元素下面。这是一条很严格的规范。“前面”在这里是指从页面源代码角度而言,作用于清除元素前面的浮动元素。

直到2004年11月份,Firefox浏览器仍然只清除从视觉角度在垂直方向上位于清除元素之上的浮动元素,而不是清除前面所有的浮动元素,这是不标准的方式。这意味着在这些早期版本的Gecko浏览器中,你可以将一个浮动列放置在屏幕的一边,而在另一列(可能是另一个浮动列)里边,你可以清除该列内部的浮动,而这样做不会导致设置清除的元素掉到前面说的浮动列下面。只有Gecko浏览器有这个问题,当这个问题出现时在页面上是显而易见的。Gecko通常是个不错的浏览器,但在这个问题上它却是个罪魁祸首。看,IE并不总是个坏家伙!

然而,本文讨论的简洁清除浮动方式有些使问题复杂化了,因为在IE中事实上不起清除作用,而被更正后的Gecko浏览器却会清除前面的浮动。


Oh不!你能看到在假定的浮动页面中将会发生什么事吗?IE,并没有正真地清除元素,看起来不错。而在新版本的Gecko浏览器和Opera7浏览器中,在第一个盒子中用CSS产生的清除元素会将该盒子的高度向下拉伸,直到这个不可见的清除元素在垂直方向上处于前一个浮动列的底部。这会在那个本来很小的简易清除盒子中“产生”巨大空白,空白有多大依赖于相邻浮动列的实际高度。

如果你在思索怎样解决这个问题,对不起,解决不了。Gecko和Opera目前都已正确地实现清除规范,可能“产生”空白。而IE中我们使用了伪“清除”,则不会。

防止外部清除

如果你碰到了前面描述的问题,防止清除元素清除相邻浮动列的一种办法就是将清除元素的容器本身设置为浮动的。当然,一旦你将容器设置为浮动的,你就再也不须要使用本文章所讲的简易清除技术。

注意当用于构造每一列的元素都是浮动的时,IE中的浮动bug并不会出现。因此用全浮动的方式进行列布局设计是容易实现的,至少是对于固定宽度的布局来说。

Them That Done It

Thanks to Tony Aslett for showing us the way. His site, csscreator.com is a killer CSS forum where newbies and gurus alike hang out and exchange CSS know-how. Tony's original demo page for this method can be found here, and the relevant forum thread is here.

Kudos also to Doug for pointing out the "period problem" in FireFox, and to Mark Hadley for that elegant IE/Mac fix, and to Matt Keogh for showing how "inline-table" also fixes IE/Mac while using an already-approved CSS property. Once more the CSS community comes thru for us all! :-)


Big John Design   Contact Us ©positioniseverything.net
Last updated: July 2, 2008
Created May 14, 2004