CSS浮動float詳解
CSS里浮動float是個概念比較曖昧的屬性,擼主最早對浮動float的認識是基于布局的,認為float元素就是用于: “讓block元素無視float元素,讓inline元素讓流水一樣圍繞著float元素” 來實現浮動布局。現在想想,當初真是圖樣圖森破。
其實這個屬性擼主一直是比較模糊的,感覺似懂非懂。本著和自己死磕的精神,在參考了許多大神的博文后,將我的理解整理歸納在本文中,希望能可以幫助到你。當然擼主水平有限,如有錯誤敬請指出。
以下是網上大神關于float的優秀文章:
浮動float的本意:
傳統如C++,Java等編程語言一個API可能只能對應做一件事,即使有了模板和泛型編程,通常也只能做某一類事。但CSS是門相當靈活的語言。某個CSS屬性被用于的場景,可能會完全違背當初創造該CSS屬性的本意。在CSS的世界里,想實現某個效果,會有很多方法。那究竟選擇哪種方法呢?有很多判斷標準,如重繪,如回流,如極簡主義。擼主也有一個不成熟的判斷標準就是: 根據該CSS屬性被創造時的本意,該用哪個屬性就用哪個屬性。
那浮動float的本意是什么呢?是: 讓文字像流水一樣環繞浮動元素。
怎樣才能實現該效果呢?用 包裹性 和 高度欺騙
特性一:包裹性
例1:首先來看浮動float的包裹性,所謂一圖勝千言:
相關代碼非常簡單:
<div style="border:4px solid blue;">
<img src="img/25/1.jpg" />
</div>
<div style="border:4px solid red;float:left;">
<img src="img/25/2.jpg" />
</div>
所謂包裹性一目了然。block元素不指定width的話,默認是100%,一旦讓該div浮動起來,立刻會像inline元素一樣產生包裹性,寬度會跟隨內容自適應。(這也是通常float元素需要手動指定width的原因)
再加上一個div的話,效果如下:
<div style="border:4px solid blue;">
<img src="img/25/1.jpg" />
</div>
<div style="border:4px solid red;float:left;">
<img src="img/25/2.jpg" />
</div>
<div style="border:4px solid green;">
<img src="img/25/3.jpg" />
</div>
效果非常近似于display:inline-block。但相比之下,浮動能設定為左浮和右浮,但display:inline-block都是從左到右排列的。(還有些細微差別,兩個display:inline-block間會有空隙,但兩個float間沒有。這不是本篇的主題,暫時略過)
特性二:高度欺騙
(首先聲明:其實是CSS層級在起作用,但CSS層級適合單獨寫一篇,內容實在太多,不適合在這里展開,就理解為高度欺騙吧)
例1中浮動float被設在了外圍div上,因此高度欺騙性沒體現出來。現在給內層img元素設定float。所謂一圖勝千言:
<div style="border:4px solid blue;">
<img src="img/25/1.jpg" />
</div>
<div style="border:4px solid red;">
<img style="border:4px solid yellow;float:left;" src="img/25/2.jpg" />
</div>
和例子1唯一的區別就是:將外層div的float移到內層img中。這下高度欺騙性體現出來了。例1中給外層div加上浮動,因此外層div會有包裹性,其內容是img圖片,所以可以看到紅色邊框包裹著img。
例2中外層div沒有了浮動,因此紅色邊框寬度默認是100%全屏。其內容img由于加上了float,使得該img具有了欺騙性。float給img施了個障眼法,讓該img的inline-height高度塌陷為0了。這樣外層div計算高度時,認為img的高度為0,相當于div的content的高度為0,因此紅色邊框看起來是一條直線。
但請注意障眼法畢竟是障眼法,并不是真的讓img的高度塌陷為0了,可以看到上圖中img的黃色邊框還是有正常高度的。如果給div里加點文字,效果如下:
可以看到,外層div在沒有手動設定height的前提下,其高度是由內部content的最大高度決定的,由于img的float使得img具有高度塌陷的欺騙性,讓div誤以為img的line-height為0,因此div的高度就是文字的匿名inline-box的inline-height。
因此浮動并不是讓元素的高度塌陷了,而是讓元素具有高度塌陷的欺騙性。騙誰?騙別人!但騙不了自己,元素自身還是有高度的(見上圖的黃框)。
回過頭再看看浮動float的本意:讓文字像流水一樣環繞圖片。重要的事情多看幾遍...給div設定一個width:200px,并加點文字吧:
這就是浮動元素的本意。該效果是很難被其他CSS屬性等價地模擬的。
但就像開頭說的,CSS強大的靈活性使得很多CSS屬性被用于了創造者都沒想到的場景。以float為例,就被廣泛用于了布局。是好是壞呢?不知道!西紅柿臭雞蛋先別急著扔。既然擼主不知道,還廢話什么?先看看float布局的問題。渣浪微博改版前的好友列表用浮動布局,效果如下:
<ul>
<li style="width:138px;margin:0 10px;text-align: center;float:left;">
<div><img src="img/25/1.jpg" />尼古拉斯.旺財</div>
</li>
<li style="width:138px;margin:0 10px;text-align: center;float:left;">
<div><img src="img/25/2.jpg" />功夫熊貓</div>
</li>
<li style="width:138px;margin:0 10px;text-align: center;float:left;">
<div><img src="img/25/3.jpg" />月野兔</div>
</li>
<li style="width:138px;margin:0 10px;text-align: center;float:left;">
<div><img src="img/25/4.jpg" />貓女郎</div>
</li>
</ul>
每個li都設為浮動和定寬,實現了水平布局。但如果好友再長點呢?效果如下:
錯行啦!常見的修正方案是手動設定一個高度,讓文字固定顯示一行,用裁掉超行文字的代價以避免錯行問題。在擼主看來這就是讓CSS屬性用于不合原意處的局限性。設固定高度是OK,但如果哪天設計師覺得姓名需要顯示兩行呢,那固定高度就需要重新計算重新變。如果設計師覺得需要拓寬俄羅斯市場,姓名要顯示三行呢?再把固定高度改大點。如果未來Boss腦袋一拍,咦,能不能高度自適應呢?由姓名最大高度的好友來決定每行的高度。你是不是會有準備一下簡歷的沖動?
當然現實沒這么夸張,高度自適應是個爛大街的技術,將浮動float改成 display:inline-block 就行了,效果如下:
代碼只需將上面float:left;替換成display: inline-block;,沒對齊只需給li加上個vertical-align: top;(上面提到過,相比float,display:inline-block中間會有空隙,眼神好的可以從圖中就能看出來,解決方案不是本篇的主題,可以問度娘)。這下高度自適應了,每行的高度都是以名字最長的高度為準。
回過頭看用float來水平布局。是好是壞呢?好處是上手簡單,隨便什么程度的CSSer都能搞定。壞處是有時需要定高難以自適應。而display:inline-block;屬性可是根正苗紅的水平布局的屬性,可以用其替代float。讓float盡量多的干其本職工作: 讓文字像流水一般環繞浮動元素 。所以擼主不知道答案。或許也根本沒有正確答案,不停的推翻原有的認識和想法人才能進步。
PS:用浮動布局還有個壞處就是IE6下可能會有問題。但在IE6橫行時期,擼主經驗尚淺,現在項目里早已明確棄用IE6,擼主也懶得挖墳驗證了。
清除浮動:
這個相對比較簡單了。用clear即可。稍微要注意的是,clear是僅作用于當前元素,例如元素A是浮動元素,靠左顯示。元素B是block元素緊跟在A后面。此時要清除浮動,是在B上設clear:left。你在A上設clear:right是沒有用的,因為A的右邊沒有浮動元素。
但真這么簡單嗎?圖樣圖森破。
先明確一個概念,用clear確實能達到我們期望的清除浮動的效果,這點沒異議。但深入點看,究竟是清除了什么樣的浮動呢?一圖勝千言:
代碼:(給頁腳加上clear:left)
<div style="border:4px solid blue;">
<div style="width:200px;border:4px solid red;float:left;">
我是浮動元素1
</div>
<div style="width:200px;border:4px solid yellow;float:left;">
我是浮動元素2
</div>
</div>
<div style="border:4px solid gray;clear:left;">我是頁腳</div>
因為浮動元素的高度欺騙性,導致外層div失去了高度(藍色邊框成了一條線)。為了讓頁腳顯示到浮動元素下面,對頁腳應用了clear:left。這是常規做法,沒有任何新奇之處。但是外層div的高度仍舊處于塌陷狀態,我們腦海真真正期望的清除浮動后的樣子難道不是下面這樣嗎?
讓清除浮動后,原本被欺騙的外層div獲得正確的高度!借用文首大神鏈接的說法,我們腦中期望的其實并不是上圖的清除浮動,而是下圖的 閉合浮動 。
閉合浮動
閉合浮動的實現方法很多,常見的是最后增加一個清除浮動的子元素:
<div style="border:4px solid blue;">
<div style="width:200px;border:4px solid red;float:left;">
我是浮動元素1
</div>
<div style="width:200px;border:4px solid yellow;float:left;">
我是浮動元素2
</div>
<div style="clear:both;"></div> //加上空白div節點來閉合浮動
</div>
<div style="border:4px solid gray;">我是頁腳</div>
缺點是會增加一個DOM節點。(話說當初擼主不知道在哪里看到這個做法時,作者并未講這么做的原因,導致擼主不明白明明頁腳加一個clear屬性就能搞定的事,為何要大動干戈加一個DOM節點)
方法二:同樣可以在最后增加一個清除浮動的br:將上面代碼中 <div style=”clear:both;”></div> 替換成 <br clear=”all” /> 即可。語義上比空的div標簽稍微好一點,但同樣會增加一個DOM節點。
方法三:父元素設置 overflow:hidden(如果你還要兼顧IE6的話,加上*zoom:1;來觸發hasLayout)
<div style="border:4px solid blue;overflow:hidden;">
<div style="width:200px;border:4px solid red;float:left;">
我是浮動元素1
</div>
<div style="width:200px;border:4px solid yellow;float:left;">
我是浮動元素2
</div>
</div>
<div style="border:4px solid gray;">我是頁腳</div>
這看起來很奇怪。因為子元素的浮動的高度欺騙,導致父元素誤認為content高度為0(即藍色邊框為一條線),所以父元素設成overflow:hidden溢出隱藏的話,直覺上應該子元素由于溢出導致不顯示才對,即整個頁面只顯示頁腳。但實際效果,父元素設成overflow:hidden溢出隱藏后,竟然神奇地出現了閉合浮動的效果(藍色邊框正確獲得了高度)。這是怎么回事呢?靠的是BFC,但BFC說起來又是很長一篇,先略過。你可以先簡單地這么理解:瀏覽器廠商認為要讓超出邊框部分可以被修剪掉,那么前提就是父元素要正確獲得高度,即父元素不能被欺騙導致高度塌陷。瀏覽器正確獲得子元素的高度后給父元素重新設置高度。雖然權威解釋肯定是BFC,但擼主這樣理解了很久...
方法四:同上面將父元素設置 的overflow:hidden改成auto,不贅述
方法五:父元素也設成float。這樣確實實現了閉合浮動,但頁腳將上移,所以頁腳仍舊需要clear:left。還有個缺點是由于浮動的包裹性,你確定父元素真的設成float對頁面布局不會產生影響嗎?
方法六:父元素設置display:table。效果OK,頁腳也不需要設clear:left,但父元素的盒子模型被改變了,請先確認下這樣的改動對頁面布局不會產生影響嗎?
方法七:用:after偽元素,思路是用:after元素在div后面插入一個隱藏文本”.”,隱藏文本用clear來實現閉合浮動:
.clearfix:after {
clear: both;
content: "."; //你頭可以改成其他任意文本如“abc”
display: block;
height: 0; //高度為0且hidden讓該文本徹底隱藏
visibility: hidden;
}
.clearfix {
*zoom: 1;
}
<div style="border:4px solid blue;" class="clearfix">
<div style="width:200px; border:4px solid red; float:left;">
我是浮動元素1
</div>
<div style="width:200px; border:4px solid yellow; float:left;">
我是浮動元素2
</div>
</div>
<div style="border:4px solid gray;">我是頁腳</div>
這個方法很不錯,就是相比上面的方法,理解起來稍微有一點點難度。但也僅增加一點點而已。
總結:
第一篇文章啰嗦幾句。其實本文開頭鏈接的幾篇博文寫的比我好,為何我要寫這篇博文?是我自信能比大神們寫的更好嗎?當然不是。之所以寫技術博文是因為這是一個非常好的學習總結的方式。就在寫之前我仍舊以為已經非常了解float了,但真要提筆寫,又發現自己在非常多的細節方面還是一知半解。只有真正靜下心來,邊思考邊做demo驗證邊總結,才能將自己的知識總結成一篇博文。
技術博文不是散文,必須條理清晰,結構嚴謹,經得起推敲,不能滿嘴跑火車,否則就誤人子弟了。以前看優秀的文章時,偶爾會看到作者吐槽:這段話足足寫了一下午。這篇文章寫了三天三夜。看著文章字數又不多,最多10分鐘就看完了,但如果你沒有真正提筆寫過,你可能體會不到期間的困難。
作為一個起步非常晚的前端工程師(I know…I late to the Party.)我只能死磕自己。
來自:http://www.jianshu.com/p/07eb19957991