深入理解BFC和Margin Collapse

原文出處:http://www.w3cplus.com/css/understanding-bfc-and-margin-collapse.html


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
<div class="box">
    <div class="img">image</div>
    <p class="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
<div class="wrapper overHid">
    <div class="big-box blue">non-float</div>
    <div class="middle-box green floatL">
        <div class="small-box red"></div>
        float left
    </div>
</div>

這裏寫圖片描述

但是浮動元素脫離了當前的BFC並不影響它後面的兄弟元素,後面的兄弟元素與浮動元素前面的元素依然在同一個BFC當中,所以,它們之間的margin還是會摺疊的。下面我們對上面的demo做一下修改:

<div class="wrapper overHid">
    <div class="big-box">non-float</div>
    <div class="middle-box green floatL">float left</div>
    <div class="middle-box red">non-clear</div>
</div>

這裏寫圖片描述

從上面這個修改後的demo中可以看出,紅色的塊盒在沒有清楚浮動的情況下,它的margin-top和藍色塊盒的margin-bottom產生了摺疊,這證明了我上面的結論。

下面我們來談談 ‘clearance’ 這個神奇的東西,當浮動元素之後的元素設置clear以閉合相關方向的浮動時,根據w3c規範規定,閉合浮動的元素會在其margin-top以上產生一定的空隙(clearance,如下圖),該空隙會阻止元素margin-top的摺疊,並作爲間距存在於元素的margin-top的上方。關於這個間距的計算稍微有點複雜,但實際工作中你並不需要去計算它,我們先來看看例子吧:

<div class="wrapper overHid">
    <div class="big-box" style="box-shadow:0 20px 0 rgba(0,0,255,0.2);">non-float</div>
    <div class="middle-box green floatL" style="opacity:0.6">float left</div>
    <div class="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’。

參考資料:
https://www.w3.org/TR/CSS2/box.html
https://www.w3.org/TR/CSS2/visuren.html#block-formatting
https://www.w3.org/TR/CSS2/box.html#collapsing-margins

發佈了40 篇原創文章 · 獲贊 15 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章