BFC的通俗理解:
Block Formatting Context(塊級格式化上下文)是W3C CSS 2.1 規范中的一個概念,它決定了元素如何對其內容進行定位,以及與其他元素的關系和相互作用。
簡單來講,我們可以把它理解為,我們在進行盒模型布局的時候,如果一個元素符合了成為BFC的條件,該元素成為一個隔離了的獨立容器,元素內部元素會垂直的沿着其父元素的邊框排列,和外部元素互不影響 。比如浮動元素會觸發BFC,浮動元素內部的子元素主要受到該浮動元素的影響,而兩個浮動元素之間是互不影響的。
在CSS3 中,BFC 叫做Flow Root。在早期的ie中也有類似的概念haslayout IE6、7的很多布局產生的bug(如3px間隙、絕對定位的繼承寬度)都可以通過觸發hasLayout修復,比較推薦的方法為zoom:1與height:1%,不會破壞已有的樣式,相信大家對它並不陌生。
同樣的以往集中在float、絕對定位、margin collaspe中的很多困惑,在理解了bfc后,都能夠被我們一一解除 。
BFC規范中的定義:
w3c規范對BFC的解釋:
浮動元素和絕對定位元素,不是塊級盒子的塊容器 (如 inline-blocks, table-cells, 和 table-captions),以及設置了overflow屬性(除了visible)的塊級盒子 ,都會為他們的內容創建新的BFC(塊級格式上下文)。
在BFC中,盒子從頂端開始垂直地 一個接一個地排列,兩個盒子之間的垂直的間隙是由他們的margin 值所決定的。在一個BFC中,兩個相鄰的塊級盒子的垂直外邊距會產生折疊。
在BFC中,每一個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)(對於從右到左的格式來說,則觸碰到右邊緣)。
觸發BFC的方法:
float 元素
position(absolute,fixed)
display (table-cell,table-caption,inline-block)
overflow 除了visible 以外的值(hidden,auto,scroll )
fieldset元素
早期IE的hasLayout會觸發一個新的block formatting context
BFC的特性
- 邊緣不和浮動元素重疊
- 不存在collapsing margins問題
第一個特性特別有用,因為元素觸發了BFC的話,就不會被float元素覆蓋,當子元素全部浮動的時候也能夠正確地包含了
第二個margin不會疊加的特性,可以理解為兩個處於普通流的盒子,會有margin疊加的問題,是因為他們屬於相同的BFC,當他自身創建了一個新的BFC時,這個問題就不存在了
BFC的常見應用
1.通過邊緣不和浮動元素重疊的特性,實現兩欄結構。
如果一個浮動元素后面跟着一個非浮動的元素,那么就會產生一個覆蓋的現象,通過觸發BFC來清除覆蓋,很多自適應的兩欄布局就是這么做的。
下面我們來看左邊圖片+右邊信息的示例:
// CSS div,p{margin:0;padding:0;} .box {width:320px;} .img {width: 80px;height: 80px;margin-right:10px;float:left;} .info {background: #ff9;color: #404040;} // HTML <div class="box"> <div class="img"><img src="http://wwc.taobaocdn.com/avatar/getAvatar.do?userNick=feihu2987&width=80&height=80&type=sns" width="80" height="80" alt="頭像"></div> <p class="info">風吹亮雪花,吹白我們的頭發,當初說一起闖天下,你們還記得嗎?</p> </div>
一般情況下它呈現出我們所樂意看到的樣子:
但隨着文字信息增多后,會變地非常的糟糕:
很明顯,這是因為info類里面的文字受到了浮動元素的影響,但這並不是我們所期望的。此時我們可以為P元素的內容建立一個BFC,讓其內容消除對外界浮動元素的影響。根據上文所知,只要給info元素添加overflow:hidden;即可為其內容建立新的BFC。當然你也可以通過其他方法來建立。其效果如下:
至此,我們可以拋棄計算設置左邊邊距,或是設置右邊浮動再清除父元素的浮動這些弱爆的方式了。
demo地址: http://jsfiddle.net/k9u5x/
當然考慮到ie6 7 的兼容性,我們同時觸發一下 haslayout就再好不過了,更多示例請看豆瓣小組和閱讀中,很多地方使用了此方式:
http://www.douban.com/group/explore/culture
.channel-item .bd, .channel-item .block,.channel-item .block p, .mod .hd, .channel-group-rec li, .channel-group-rec li .info { overflow: hidden; zoom: 1; }
2.清除元素內部浮動
只要把父元素設為BFC就可以清理子元素的浮動了,同樣的因為IE6-7不支持BFC,因此要設置zoom:1來觸發hasLayout 閉合浮動。
我們最常見的兼容用法就是在父元素上添加class clearfix ;
.clearfix {*zoom:1} .clearfix:after{content:"\0020";display:block;height:0;clear:both;visibility:hidden}
3.解決合並外邊距的問題
在CSS當中,相鄰的兩個盒子(可能是兄弟關系也可能是父子關系)的外邊距可以結合成一個單獨的外邊距。這種合並外邊距的方式被稱為折疊,並且因而所結合成的外邊距稱為折疊外邊距。
按照BFC的定義,只有同屬於一個BFC時,兩個元素才有可能發生垂直Margin的重疊,這個包括相鄰元素,嵌套元素,只要他們之間沒有線盒(其實就是非空內容),沒有間隙(clearance,設置clear以閉合相關方向的浮動 ),沒有padding和border將他們分隔開就會發生margin重疊。
因此要解決margin重疊問題,只要讓它們不在同一個BFC就行了,但是對於兩個相鄰元素來說,意義不大,沒有必要給它們加個外殼,但是對於嵌套元素來說就很有必要了,只要把父元素設為BFC就可以了。這樣子元素的margin就不會和父元素的margin發生折疊了。
折疊的結果:
兩個相鄰的外邊距都是正數時,折疊結果是它們兩者之間較大的值( 10px 20px 結果為20px )。
兩個相鄰的外邊距都是負數時,折疊結果是兩者絕對值較大的值( -10px -20px 結果是-20px )。
兩個外邊距一正一負時,折疊結果是兩者的相加的和( 10px -20px 結果是10px )。
產生折疊的必備條件:margin必須是邊緣毗鄰的
而根據w3c規范,兩個margin是垂直毗鄰的必須滿足以下條件:
元素的margin-top與其第一個常規文檔流的子元素的margin-top
元素的margin-bottom與其下一個常規文檔流的兄弟元素的margin-top
height為auto的元素的margin-bottom與其最后一個常規文檔流的子元素的margin-bottom
高度為0並且最小高度也為0,不包含常規文檔流的子元素,並且自身沒有建立新的BFC的元素的margin-top和margin-bottom
只要觸發Block Formatting Contexts的條件 或者是使用非空內容,空隙(clearance),padding和border將他們分隔開margin便不會折疊。
分析一:浮動和絕對定位不與任何元素產生 margin 折疊
原因:浮動元素和絕對定位元素不與其他盒子產生外邊距折疊是因為元素會脫離當前的文檔流,違反了上面所述的兩個margin是鄰接的條件同時,又因為浮動和絕對定位會使元素為它的內容創建新的BFC,因此該元素和子元素所處的BFC是不相同的,因此也不會產生margin的折疊。
分析二:inline-block元素與其兄弟元素、子元素和父元素的外邊距都不會折疊(包括其父元素和子元素)
inline-block不符合w3c規范所說元素必須是塊級盒子的條件,因為規范中又說明,塊級盒子的display屬性必須是以下三種之一:'block', 'list-item', 和 'table'。
分析三:設置有些屬性(如float、position、display)會同時形成BFC與觸發hasLayout,或者在>=IE7時也可以設置overflow同時搞定兩者。
在IE7以下,只觸發(形成)了其中一個,也最好同時觸發(形成)另一個,以保證瀏覽器的兼容。
簡單的觸發方法:{overflow: hidden;zoom:1;}
如果感覺閱讀此文后,有一定的啟發和收獲,勞駕您推薦下,在此謝過,歡迎留言交流。