浮動(float),1個我們即愛又恨的屬性。愛,由于通過浮動,我們能很方便地布局; 恨,浮動以后遺留下來太多的問題需要解決,特別是IE6⑺(以下無特殊說明均指 windows 平臺的 IE閱讀器)。或許很多人都有這樣的疑問,浮動從何而來?我們為什么要清除浮動?清除浮動的原理是甚么?本文將1步1步地深入剖析其中的奧秘,讓浮動使用起來更加得心應手。
1、清除浮動 還是 閉合浮動 (Enclosing float orClearing float)?
很多人都已習慣稱之為清除浮動,之前我也1直這么叫著,但是確切地來講是不準確的。我們應當用嚴謹的態度來對待代碼,也能更好地幫助我們理解開頭的3個問題。
1)清除浮動:清除對應的單詞是 clear,對應CSS中的屬性是 clear:left | right | both | none;
2)閉合浮動:更確切的含義是使浮動元素閉合,從而減少浮動帶來的影響。
二者的區分 請看優雅的 Demo
通過以上實例發現,其實我們想要到達的效果更確切地說是閉合浮動,而不是單純的清除浮動,在footer上設置clear:both清除浮動其實不能解決warp高度塌陷的問題。
結論:用閉合浮動比清除浮動更加嚴謹,所以后文中統1稱之為:閉合浮動。
2、為什么要清除浮動?
要解答這個問題,我們得先說說CSS中的定位機制:普通流,浮動,絕對定位 (其中"position:fixed"是 "position:absolute" 的1個子類)。
1)普通流:很多人或文章稱之為文檔流或普通文檔流,其實標準里根本就沒有這個詞。如果把文檔流直譯為英文就是 document flow ,但標準里只有另外一個詞,叫做 普通流 (normal flow),或稱之為常規流。但似乎大家更習慣文檔流的稱呼,由于很多中文翻譯的書就是這么來的。比如《CSS Mastery》,英文原書中至始至終都只有普通流 normal flow(普通流) 這1詞,歷來沒出現過document flow (文檔流)
2)浮動:浮動的框可以左右移動,直至它的外邊沿遇到包括框或另外一個浮動框的邊沿。浮動框不屬于文檔中的普通流,當1個元素浮動以后,不會影響到 塊級框的布局而只會影響內聯框(通常是文本)的排列,文檔中的普通流就會表現得和浮動框不存在1樣,當浮動框高度超越包括框的時候,也就會出現包括框不會 自動伸高來閉合浮動元素(“高度塌陷”現象)。顧名思義,就是漂浮于普通流之上,像浮云1樣,但是只能左右浮動。
正是由于浮動的這類特性,致使本屬于普通流中的元素浮動以后,包括框內部由于不存在其他普通流元素了,也就表現出高度為0(高度塌陷)。在實際布局中,常常這其實不是我們所希望的,所以需要閉合浮動元素,使其包括框表現出正常的高度。
絕對定位就不多說了,不在本文討論范圍以內,下回分解。
3、清除浮動的原理——了解 hasLayout 和 Block formattingcontexts
先看1下清算浮動的各種方法:
1)添加額外標簽
這是在學校老師就告知我們的 1種方法,通過在浮動元素末尾添加1個空的標簽例如 <div style=”clear:both”></div>,其他標簽br等亦可。
復制代碼
代碼以下:
<div class="warp" id="float1">
<h2>1)添加額外標簽</h2>
<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>
/[code]
優點:通俗易懂,容易掌握
缺點:可以想象通過此方法,會添加多少無意義的空標簽,有背結構與表現的分離,在后期保護中將是噩夢,這是堅決不能忍耐的,所以你看了這篇文章以后還是建議不要用了吧。
2)使用 br標簽和其本身的 html屬性
這個方法有些小眾,br 有 clear=“all | left| right | none” 屬性
[code]
<div class="warp" id="float2">
<h2>2)使用 br標簽和其本身的 html屬性</h2>
<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>
優雅的 Demo
優點:比空標簽方式語義稍強,代碼量較少
缺點:一樣有背結構與表現的分離,不推薦使用
3)父元素設置 overflow:hidden
通過設置父元素overflow值設置為hidden;在IE6中還需要觸發 hasLayout ,例如 zoom:1;
復制代碼
代碼以下:
<div class="warp" id="float3" style="overflow:hidden;*zoom:1;">
<h2>3)父元素設置 overflow </h2>
<div class="main left">.main{float:left;}</div>
<div class="side left">.side{float:right;}</div>
</div>
<div class="footer">.footer</div>
優點:不存在結構和語義化問題,代碼量極少
缺點:內容增多時候容易造成不會自動換行致使內容被隱藏掉,沒法顯示需要溢出的元素;04年POPO就發現overflow:hidden會致使中鍵失效,這是我作為1個多標簽閱讀控所不能接受的。所以還是不要使用了
4)父元素設置 overflow:auto 屬性
一樣IE6需要觸發hasLayout,演示和3差不多
優點:不存在結構和語義化問題,代碼量極少
缺點:多個嵌套后,firefox某些情況會造成內容全選;IE中 mouseover 造成寬度改變時會出現最外層模塊有轉動條等,firefox初期版本會無故產生focus等, 請看 嗷嗷的 Demo ,不要使用
5)父元素也設置浮動
優點:不存在結構和語義化問題,代碼量極少
缺點:使得與父元素相鄰的元素的布局會遭到影響,不可能1直浮動到body,不推薦使用
6)父元素設置display:table
優雅的 Demo
優點:結構語義化完全正確,代碼量極少
缺點:盒模型屬性已改變,由此釀成的1系列問題,得不償失,不推薦使用
7)使用:after 偽元素
需要注意的是 :after是偽元素(Pseudo-Element),不是偽類(某些CSS手冊里面稱之為“偽對象”),很多清除浮動大全之類的文章都稱之為偽類,不過csser要嚴謹1點,這是1種態度。
由于IE6⑺不支持:after,使用 zoom:1觸發 hasLayout。
該方法源自于: How To Clear Floats Without StructuralMarkup
原文全部代碼以下:
復制代碼
代碼以下:
<style type="text/css">
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.clearfix {display: inline-block;} /* for IE/Mac */
</style>
<!--[if IE]> <style type="text/css">
.clearfix {zoom: 1;/* triggers hasLayout */
display: block;/* resets display for IE/Win */}
</style>
<![endif]-->
鑒于 IE/Mac的市場占有率極低,我們直接疏忽掉,最后精簡的代碼以下:
復制代碼
代碼以下:
.clearfix:after {content:"."; display:block; height:0;visibility:hidden; clear:both; }
.clearfix { *zoom:1; }
優點:結構和語義化完全正確,代碼量居中
缺點:復用方式不當會造成代碼量增加
小結
通過對照,我們不難發現,其實以上羅列的方法,不過有兩類:
其1,通過在浮動元素的末尾添加1個空元素,設置 clear:both屬性,after偽元素其實也是通過
content 在元素的后面生成了內容為1個點的塊級元素;
其2,通過設置父元素 overflow
或display:table屬性來閉合浮動,我們來探討1下這里面的原理。
在CSS2.1里面有1個很重要的概念,但是國內的技術博客介紹到的比較少,那就是 Block formatting contexts (塊級格式化上下文),以下簡稱 BFC。
CSS3里面對這個規范做了改動,稱之為:flow root,并且對觸發條件進行了進1步說明。
那末如何觸發BFC呢?
float 除none之外的值
overflow 除visible 之外的值(hidden,auto,scroll )
display (table-cell,table-caption,inline-block)
position(absolute,fixed)
fieldset元素
需要注意的是,display:table 本身其實不會創建BFC,但是它會產生匿名框(anonymous boxes),而匿名框中的display:table-cell可以創建新的BFC,換句話說,觸發塊級格式化上下文的是匿名框,而不是 display:table。所以通過display:table和display:table-cell創建的BFC效果是不1樣的。
fieldset 元素在www.w3.org里目前沒有任何有關這個觸發行動的信息,直到HTML5標準里才出現。有些閱讀器bugs(Webkit,Mozilla)提到過這個觸發行動,但是沒有任何官方聲明。實際上,即便fieldset在大多數的閱讀器上都能創建新的塊級格式化上下文,開發者也不應當把這當作是天經地義的。CSS 2.1沒有定義哪一種屬性適用于表單控件,也沒有定義如何使用CSS來給它們添加樣式。用戶代理可能會給這些屬性利用CSS屬性,建議開發者們把這類支持當作實驗性質的,更高版本的CSS可能會進1步規范這個。
BFC的特性:
1)塊級格式化上下文會禁止外邊距疊加
當兩個相鄰的塊框在同1個塊級格式化上下文中時,它們之間垂直方向的外邊距會產生疊加。換句話說,如果這兩個相鄰的塊框不屬于同1個塊級格式化上下文,那末它們的外邊距就不會疊加。
2)塊級格式化上下文不會堆疊浮動元素
根據規定,1個塊級格式化上下文的邊框不能和它里面的元素的外邊距堆疊。這就意味著閱讀器將會給塊級格式化上下文創建隱式的外邊距來禁止它和浮動元素的外邊距疊加。由于這個緣由,當給1個挨著浮動的塊級格式化上下文添加負的外邊距時將會不起作用(Webkit和IE6在這點上有1個問題——可以看這個測試用例)。
3)塊級格式化上下文通常可以包括浮動
詳見: W3C CSS2.1 - 10.6.7 'Auto' heights forblock formatting context roots
通俗地來講:創建了 BFC的元素就是1個獨立的盒子,里面的子元素不會在布局上影響外面的元素,反之亦然,同時BFC任然屬于文檔中的普通流。
至此,您也許明白了為何 overflow:hidden或auto可以閉合浮動了,真是由于父元素創建了新的BFC。對張鑫旭在對《overflow與zoom”清除浮動”的1些認識 》1文中對用包裹來解釋閉合浮動的原理,我覺得是不夠嚴謹的,而且沒有根據。并且說道“Firefox等閱讀器并沒有haslayout的概念”,那末現代閱讀器是有BFC的,從表現上來講,hasLayout 可以同等于 BFC。
IE6⑺的顯示引擎使用的是1個稱為布局(layout)的內部概念,由于這個顯示引擎本身存在很多的缺點,直接致使了IE6⑺的很多顯示 bug。當我們說1個元素“得到 layout”,或說1個元素“具有 layout” 的時候,我們的意思是指它的微軟專有屬性 hasLayouthttp://msdn.microsoft.com/worksh ... rties/haslayout.asp 為此被設為了 true 。IE6⑺使用布局的概念來控制元素的尺寸和定位,那些具有布局(have layout)的元素負責本身及其子元素的尺寸設置和定位。如果1個元素的
hasLayout 為false,那末它的尺寸和位置由最近具有布局的先人元素控制。
觸發hasLayout的條件:
position: absolute
float: left|right
display: inline-block
width: 除 “auto” 外的任意值
height: 除 “auto” 外的任意值 (例如很多人清除浮動會用到 height: 1% )
zoom: 除 “normal” 外的任意值 (MSDN)http://msdn.microsoft.com/worksh ... properties/zoom.asp
writing-mode: tb-rl (MSDN)http://msdn.microsoft.com/worksh ... ies/writingmode.asp
在 IE7 中,overflow 也變成了1個 layout 觸發器:
overflow: hidden|scroll|auto ( 這個屬性在IE之前版本中沒有觸發 layout 的功能。 )
overflow-x|-y: hidden|scroll|auto (CSS3 盒模型中的屬性,還沒有得到閱讀器的廣泛支持。他們在之前IE版本中一樣沒有觸發 layout 的功能)
hasLayout更詳細的解釋請參見 old9翻譯的 大名鼎鼎的 《On having layout》1文(英文原文:http://www.satzansatz.de/cssd/onhavinglayout.htm),由于old9博客被墻,中文版地址:
IE8使用了全新的顯示引擎,據稱不使用 hasLayout屬性了,因此解決了很多深惡痛絕的bug。
綜上所述:
在支持BFC的閱讀器(IE8+,firefox,chrome,safari)通過創建新的BFC閉合浮動;
在不支持 BFC的閱讀器 (IE6⑺),通過觸發 hasLayout 閉合浮動。
4、閉合浮動方法——精益求精
上面已羅列了7種閉合浮動的方法,通過第3節分析的原理,我們發現其實更多的:display:table- cell,display:inline-block等只要觸發了BFC的屬性值都可以閉合浮動。從各個方面比較,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:"."生成內容作為最后1個元素,至于content里面是點還是其他都是可以的,例如oocss里面就有經典的 content:"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",有些版本可能content 里面內容為空,1絲冰冷是不推薦這樣做的,firefox直到7.0content:”" 依然會產生額外的空隙;
5)zoom:1 觸發IE hasLayout。
通過分析發現,除clear:both用來清除浮動的,其他代碼不過都是為了隱藏掉content生成的內容,這也就是其他版本的閉合浮動為何會有font-size:0,line-height:0。
精益求精方案1:
相對空標簽閉合浮動的方法代碼似乎還是有些冗余,通過查詢發現Unicode字符里有1個“零寬度空格”,也就是U+200B ,這個字符本身是不可見的,所以我們完全可以省略掉 visibility:hidden了
復制代碼
代碼以下:
.clearfix:after {content:"\200B"; display:block; height:0;clear:both; }
.clearfix { *zoom:1; }.
精益求精方案2:
由Nicolas Gallagher 大濕提出來的,原文:A new micro clearfix hack,該方法也不存在firefox中空隙的問題。
復制代碼
代碼以下:
/* For modern browsers */
.cf:before,.cf:after {
content:"";
display:table;
}
.cf:after { clear:both; }/* For IE 6/7 (trigger hasLayout) */
.cf { zoom:1; }
需要注意的是:
上面的方法用到了 :before偽元素,很多人對這個有些迷惑,到底我甚么時候需要用before呢?為何方案1沒有呢?其實它是用來處理margin邊距堆疊的,由于內部元素 float 創建了BFC,致使內部元素的margin-top和 上1個盒子的margin-bottom 產生疊加。如果這不是你所希望的,那末就能夠加上before,如果只是單純的閉合浮動,after就夠了!其實不是猶如大漠《Clear Float》1文所說的:但只使用clearfix:after時在跨閱讀器兼容問題會存在1個垂直邊距疊加的bug,這不是bug,是BFC應當有的特性。
在實際開發中,改進方案1由于存在Unicode字符不合適內嵌CSS的GB2312編碼的頁面,使用方案7完全可以解決我們的需求了,改進方案2等待大家的進1步實踐。方案3、4通過overflow閉合浮動,實際上已創建了新的 塊級格式化上下文,這將致使其布局和相對浮動的行動等產生1系列的變化,清除浮動只不過是1系列變化中的1個作用而已。所以為了閉合浮動去改變全局特性,這是不明智的,帶來的風險就是1系列的bug,比如firefox 初期版本產生 focus,截斷絕對定位的層等等。始終要明白,如果單單只是需要閉合浮動,overflow就不要使用,而不是某些文章所說的“慎用”。