| 文章簡介:深入理解BFC和Margin Collapse. |
BFC的理解與應用
首先我們來看看w3c規範對BFC的解釋,其實對於這種概念的學習上,我們總是建議首先尋找官方的定義,因為原則上來說官方的才是最權威和正確的,而且還比較詳細,千萬不要因為看到英文就畏懼不前。
什麼是BFC(Block formatting contexts)
w3c規範中的BFC定義:
浮動元素和絕對位置元素,非塊級盒子的塊級容器(例如 inline-blocks, table-cells, 和 table-captions),以及overflow值不為“visiable”的塊級盒子,都會為他們的內容建立新的BFC(塊級格式上下文)。
在BFC中,盒子從頂端開始垂直地一個接一個地排列,兩個盒子之間的垂直的間隙是由他們的margin 值所決定的。在一個BFC中,兩個相鄰的塊級盒子的垂直外邊距會產生摺疊。
在BFC中,每一個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)(對於從右至左的格式來說,則觸碰到右邊緣)。
BFC的通俗理解:
首先BFC是一個名詞,是一個獨立的布局環境,我們可以理解為一個箱子(實際上是看不見摸不著的),箱子裡面物品的擺放是不受外界的影響的。轉換為BFC的理解則是:BFC中的元素的布局是不受外界的影響(我們往往利用這個特性來消除浮動元素對其非浮動的兄弟元素和其子項目帶來的影響。)並且在一個BFC中,塊盒與行盒(行盒由一行中所有的內嵌元素所組成)都會垂直的沿著其父元素的邊框排列。
BFC的運用
在w3c的規範中,除了上面的一段定義之外,BFC的相關知識點分布地比較零散,但基本集中在float、絕對位置、margin collaspe中。下面我們來看看如何應用到BFC來解決問題。
在很多網站中,我們經常會看到這樣的一種,左邊圖片+右邊資訊的兩欄結構,下面我們來看看如何利用BFC來實現。
首先我們給出這樣的結構:
//CSS .box {width:210px;border: 1px solid #000;float: left;} .img {width: 100px;height: 100px;background: #696;float: left;} .info {background: #ccc;color: #fff;} //HTML <divclass="box"><divclass="img">image</div><pclass="info">資訊資訊資訊資訊資訊資訊資訊資訊資訊資訊資訊信</p></div>
一般情況下它呈現出我們所樂意看到的樣子:
但隨著文字資訊增多後,會變地非常的糟糕:
很明顯,這是因為info類裡面的文字受到了浮動元素的影響,但這並不是我們所期望的。此時我們可以為P元素的內容建立一個BFC,讓其內容消除對外界浮動元素的影響。根據上文所知,只要給info元素添加overflow:hidden;即可為其內容建立新的BFC。當然你也可以通過其他方法來建立。其效果如下:
合并外邊距與BFC
在CSS當中,相鄰的兩個盒子(可能是兄弟關係也可能是祖先關係)的外邊距可以結合成一個單獨的外邊距。這種合并外邊距的方式被稱為摺疊,並且因而所結合成的外邊距稱為摺疊外邊距。
摺疊的結果:
- 兩個相鄰的外邊距都是正數時,摺疊結果是它們兩者之間較大的值。
- 兩個相鄰的外邊距都是負數時,摺疊結果是兩者絕對值的較大值。
- 兩個外邊距一正一負時,摺疊結果是兩者的相加的和。
產生摺疊的必備條件:margin必須是鄰接的!
而根據w3c規範,兩個margin是鄰接的必須滿足以下條件:
- 必須是處於常規文檔流(非float和絕對位置)的塊級盒子,並且處於同一個BFC當中。
- 沒有線盒,沒有空隙(clearance,下面會講到),沒有padding和border將他們分隔開
- 都屬於垂直方向上相鄰的外邊距,可以是下面任意一種情況
- 元素的margin-top與其第一個常規文檔流的子項目的margin-top
- 元素的margin-bottom與其下一個常規文檔流的兄弟元素的margin-top
- height為auto的元素的margin-bottom與其最後一個常規文檔流的子項目的margin-bottom
- 高度為0並且最小高度也為0,不包含常規文檔流的子項目,並且自身沒有建立新的BFC的元素的margin-top和margin-bottom
以上的條件意味著下列的規則:
- 建立了新的BFC的元素(例如浮動元素或者"overflow"值為"visible"以外的元素)與它的子項目的外邊距不會摺疊
- 浮動元素不與任何元素的外邊距產生摺疊(包括其父元素和子項目)
- 絕對位置元素不與任何元素的外邊距產生摺疊
- inline-block元素不與任何元素的外邊距產生摺疊
- 一個常規文檔流元素的margin-bottom與它下一個常規文檔流的兄弟元素的margin-top會產生摺疊,除非它們之間存在間隙(clearance)。
- 一個常規文檔流元素的margin-top 與其第一個常規文檔流的子項目的margin-top產生摺疊,條件為父元素不包含 padding 和 border ,子項目不包含 clearance。
- 一個 "height" 為 "auto" 並且 "min-height" 為 "0"的常規文檔流元素的 margin-bottom 會與其最後一個常規文檔流子項目的 margin-bottom 摺疊,條件為父元素不包含 padding 和 border ,子項目的 margin-bottom 不與包含 clearance 的 margin-top 摺疊。
- 一個不包含border-top、border-bottom、padding-top、padding-bottom的常規文檔流元素,並且其 "height" 為 0 或 "auto", "min-height" 為 "0",其裡面也不包含行盒(line box),其自身的 margin-top 和 margin-bottom 會摺疊。
(下面我們對不產生摺疊的情況逐一分析。)
浮動和絕對位置不與任何元素產生 margin 摺疊
原因:浮動元素和絕對位置元素不與其他盒子產生外邊距摺疊是因為元素會脫離當前的文檔流,違反了上面所述的兩個margin是鄰接的條件同時,又因為浮動和絕對位置會使元素為它的內容建立新的BFC,因此該元素和子項目所處的BFC是不相同的,因此也不會產生margin的摺疊。
DEMO:
//CSS body {padding:0;margin: 0; text-align: center;} .wrapper {margin:30px;width: 450px;border:1px solid red;} .small-box {width: 50px;height: 50px;margin: 10px;background: #9cc;} .middle-box {width: 100px;height: 100px;margin: 20px;background: #99c;} .big-box {width: 120px;height: 120px;margin: 20px;background: #33e;} .floatL {float: left;} .floatR {float: right;} .clear {clear: both;} .posA {position: absolute;} .overHid{overflow: hidden;} .red {background: #f00;} .green {background: #0f0;} .blue {background: #00f;} //HTML <divclass="wrapper overHid"><divclass="big-box blue">non-float</div><divclass="middle-box green floatL"><divclass="small-box red"></div> float left </div></div>
但是浮動元素脫離了當前的BFC並不影響它後面的兄弟元素,後面的兄弟元素與浮動元素前面的元素依然在同一個BFC當中,所以,它們之間的margin還是會摺疊的。下面我們對上面的demo做一下修改:
<divclass="wrapper overHid"><divclass="big-box">non-float</div><divclass="middle-box green floatL">float left</div><divclass="middle-box red">non-clear</div></div>
從上面這個修改後的demo中可以看出,紅色的塊盒在沒有清楚浮動的情況下,它的margin-top和藍色塊盒的margin-bottom產生了摺疊,這證明了我上面的結論。
下面我們來談談 "clearance" 這個神奇的東西,當浮動元素之後的元素設定clear以閉合相關方向的浮動時,根據w3c規範規定,閉合浮動的元素會在其margin-top以上產生一定的空隙(clearance,如下圖),該空隙會阻止元素margin-top的摺疊,並作為間距存在於元素的margin-top的上方。關於這個間距的計算稍微有點複雜,但實際工作中你並不需要去計算它,我們先來看看例子吧:
<divclass="wrapper overHid"><divclass="big-box"style="box-shadow:0 20px 0 rgba(0,0,255,0.2);">non-float</div><divclass="middle-box green floatL"style="opacity:0.6">float left</div><divclass="middle-box red clear"style="margin-top:40px;box-shadow:0 -40px 0 rgba(255,0,0,0.2);">clear</div></div>
上面的圖中我們可以看到,我們為紅色塊盒設定的40px的margin-top(這裡我們通過相同高度的陰影來將其可視化)好像並沒有對紫色塊盒起作用,而且無論我們怎麼修改這個margin-top值都不會影響紅色塊盒的位置,而只由綠色塊盒的margin-bottom所決定。
也就是說,我們只需要知道,閉合浮動的元素的border-top會緊貼著相應的浮動元素的margin-bottom。
原來,通過w3c的官方規範可知,閉合浮動的塊盒在margin-top上所產生的間距(clearance)的值與該塊盒的margin-top之和應該足夠讓該塊盒垂直的跨越浮動元素的margin-bottom,使閉合浮動的塊盒的border-top恰好與浮動元素的塊盒的margin-bottom相鄰接。
用上圖例子中的相關值可以得出這樣一個式子:r-margin-top + r-clearance = g-margin-top + g-height + g-margin-bottom
PS!閉合浮動並不能使浮動元素回到原來的BFC當中!
分析二:inline-block元素與其兄弟元素、子項目和父元素的外邊距都不會摺疊(包括其父元素和子項目)
inline-block不符合w3c規範所說元素必須是塊級盒子的條件,因為規範中又說明,塊級盒子的display屬性必須是以下三種之一:"block", "list-item", 和 "table"。
參考資料
- http://www.w3.org/TR/CSS2/box.html
- http://www.w3.org/TR/CSS2/visuren.html#block-formatting
- http://www.w3.org/TR/CSS2/box.html#collapsing-margins
如需轉載,請遵守W3cplus著作權聲明,煩請註明出處:http://www.w3cplus.com/css/understanding-bfc-and-margin-collapse.html