在一個Web頁面的CSS渲染中,塊級格式化上下文 (Block Fromatting Context)是按照塊級盒子布局的。W3C對BFC的定義如下:
浮動元素和絕對定位元素,非塊級盒子的塊級容器(例如 inline-blocks, table-cells, 和 table-captions),以及overflow值不為“visiable”的塊級盒子,都會為他們的內容創建新的BFC(塊級格式上下文)。
為了便於理解,我們換一種方式來重新定義BFC。一個HTML元素要創建BFC,則滿足下列的任意一個或多個條件即可:
1、float的值不是none。
2、position的值不是static或者relative。
3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
4、overflow的值不是visible
BFC是一個獨立的布局環境,其中的元素布局是不受外界的影響,並且在一個BFC中,塊盒與行盒(行盒由一行中所有的內聯元素所組成)都會垂直的沿着其父元素的邊框排列。
怎么創建BFC
要顯示的創建一個BFC是非常簡單的,只要滿足上述4個CSS條件之一就行。例如:
<div class="container">
你的內容
</div>
在類container中添加類似 overflow: scroll,overflow: hidden,display: flex,float: left,或 display: table 的規則來顯示創建BFC。雖然添加上述的任意一條都能創建BFC,但會有一些副作用:
1、display: table 可能引發響應性問題
2、overflow: scroll 可能產生多余的滾動條
3、float: left 將把元素移至左側,並被其他元素環繞
4、overflow: hidden 將裁切溢出元素
因而無論什么時候需要創建BFC,都要基於自身的需要來考慮。對於本文,將采用 overflow: hidden 方式:
.container {
overflow: hidden;
}
再說兩點
BFC中盒子怎么對齊
如前文所說,在一個BFC中,塊盒與行盒(行盒由一行中所有的內聯元素所組成)都會垂直的沿着其父元素的邊框排列。W3C給出得規范是:
在BFC中,每一個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)(對於從右到左的格式來說,則觸碰到右邊緣)。浮動也是如此(盡管盒子里的行盒子 Line Box 可能由於浮動而變窄),除非盒子創建了一個新的BFC(在這種情況下盒子本身可能由於浮動而變窄)。
外邊距折疊
常規流布局時,盒子都是垂直排列,兩者之間的間距由各自的外邊距所決定,但不是二者外邊距之和。
<div class="container">
<p>Sibling 1</p>
<p>Sibling 2</p>
</div>
對應的CSS:
.container {
background-color: red;
overflow: hidden; /* creates a block formatting context */
}
p {
background-color: lightgreen;
margin: 10px 0;
}
在上圖中,一個紅盒子(div)包含着兩個兄弟元素(p),一個BFC已經創建了出來。
理論上,兩個p元素之間的外邊距應當是二者外邊距之和(20px)但實際上卻是10px,這是外邊距折疊(Collapsing Margins)的結果。
在CSS當中,相鄰的兩個盒子(可能是兄弟關系也可能是祖先關系)的外邊距可以結合成一個單獨的外邊距。這種合並外邊距的方式被稱為折疊,並且因而所結合成的外邊距稱為折疊外邊距。折疊的結果按照如下規則計算:
1、兩個相鄰的外邊距都是正數時,折疊結果是它們兩者之間較大的值。
2、兩個相鄰的外邊距都是負數時,折疊結果是兩者絕對值的較大值。
3、兩個外邊距一正一負時,折疊結果是兩者的相加的和。
產生折疊的必備條件:margin必須是鄰接的! (對於不產生折疊的情況,見參考文章的鏈接)
BFC可以做什么呢?
利用BFC避免外邊距折疊
BFC可能造成外邊距折疊,也可以利用它來避免這種情況。BFC產生外邊距折疊要滿足一個條件:兩個相鄰元素要處於同一個BFC中。所以,若兩個相鄰元素在不同的BFC中,就能避免外邊距折疊。
改進前面的例子:
<div class="container">
<p>Sibling 1</p>
<p>Sibling 2</p>
<p>Sibling 3</p>
</div>
對應的CSS:
.container {
background-color: red;
overflow: hidden; /* creates a block formatting context */
}
p {
background-color: lightgreen;
margin: 10px 0;
}
結果和上面一樣,由於外邊距折疊,三個相鄰P元素之間的垂直距離是10px,這是因為三個 p 標簽都從屬於同一個BFC。
修改第三個P元素,使之創建一個新的BFC:
<div class="container">
<p>Sibling 1</p>
<p>Sibling 2</p>
<div class="newBFC">
<p>Sibling 3</p>
</div>
</div>
對應的CSS:
.container {
background-color: red;
overflow: hidden; /* creates a block formatting context */
}
p {
margin: 10px 0;
background-color: lightgreen;
}
.newBFC {
overflow: hidden; /* creates new block formatting context */
}
因為第二個和第三個P元素現在分屬於不同的BFC,它們之間就不會發生外邊距折疊了。
BFC包含浮動
浮動元素是會脫離文檔流的(絕對定位元素會脫離文檔流)。如果一個沒有高度或者height是auto的容器的子元素是浮動元素,則該容器的高度是不會被撐開的。我們通常會利用偽元素(:after或者:before)來解決這個問題。BFC能包含浮動,也能解決容器高度不會被撐開的問題。
看個例子:
<div class="container">
<div>Sibling</div>
<div>Sibling</div>
</div>
CSS:
.container {
background-color: green;
}
.container div {
float: left;
background-color: lightgreen;
margin: 10px;
}
在上面這個例子中,容器沒有任何高度,並且它包不住浮動子元素,容器的高度並不會被撐開。為解決這個問題,可以在容器中創建一個BFC:
.container {
overflow: hidden; /* creates block formatting context */
background-color: green;
}
.container div {
float: left;
background-color: lightgreen;
margin: 10px;
}
現在容器可以包住浮動子元素,並且其高度會擴展至包住其子元素,在這個新的BFC中浮動元素又回歸到頁面的常規流之中了。
使用BFC避免文字環繞
如上圖所示,對於浮動元素,可能會造成文字環繞的情況(Figure1),但這並不是我們想要的布局(Figure2才是想要的)。要解決這個問題,我們可以用外邊距,但也可以用BFC。
First let us understand why the text wraps. For this we have to understand how the box model works when an element is floated. This is the part I left earlier while discussing the alignment in a block formatting context. Let us understand what is happening in Figure 1 in the diagram below:
假設HTML是:
<div class="container">
<div class="floated">
Floated div
</div>
<p>
Quae hic ut ab perferendis sit quod architecto,
dolor debitis quam rem provident aspernatur tempora
expedita.
</p>
</div>
上圖整個黑色區域表示 p 元素。p 元素沒有移位但它疊在了浮動元素之下,而p元素的文本(行盒子)卻移位了,行盒子水平變窄來給浮動元素騰出了空間。隨着文本的增加,最后文本將環繞在浮動元素之下,因為那時候行盒子不再需要移位,也就成了圖Figure1的樣子。
再回顧一下W3C的描述:
在BFC上下文中,每個盒子的左外側緊貼包含塊的左側(從右到左的格式里,則為盒子右外側緊貼包含塊右側),甚至有浮動也是如此(盡管盒子里的行盒子 Line Box 可能由於浮動而變窄),除非盒子創建了一個新的BFC(在這種情況下盒子本身可能由於浮動而變窄)。
因而,如果p元素創建一個新的BFC那它就不會再緊貼包含塊的左側了。
在多列布局中使用BFC
如果我們創建一個占滿整個容器寬度的多列布局,在某些瀏覽器中最后一列有時候會掉到下一行。這可能是因為瀏覽器四舍五入了列寬從而所有列的總寬度會超出容器。但如果我們在多列布局中的最后一列里創建一個新的BFC,它將總是占據其他列先占位完畢后剩下的空間。
例如:
<div class="container">
<div class="column">column 1</div>
<div class="column">column 2</div>
<div class="column">column 3</div>
</div>
對應的CSS:
.column {
width: 31.33%;
background-color: green;
float: left;
margin: 0 1%;
}
/* Establishing a new block formatting
context in the last column */
.column:last-child {
float: none;
overflow: hidden;
}
現在盡管盒子的寬度稍有改變,但布局不會打破。當然,對多列布局來說這不一定是個好辦法,但能避免最后一列下掉。這個問題上彈性盒或許是個更好的解決方案,但這個辦法可以用來說明元素在這些環境下的行為。