CSS中的BFC

舞動乾坤發表於2018-11-15

原文連結:CSS中的BFC

什麼是BFC

在一個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(在這種情況下盒子本身可能由於浮動而變窄)。

img

外邊距摺疊

常規流佈局時,盒子都是垂直排列,兩者之間的間距由各自的外邊距所決定,但不是二者外邊距之和。

<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;
}
複製程式碼

渲染結果如圖:

img2

在上圖中,一個紅盒子(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 */
}
複製程式碼

現在的結果如圖:

img

因為第二個和第三個P元素現在分屬於不同的BFC,它們之間就不會發生外邊距摺疊了。

BFC包含浮動

浮動元素是會脫離文件流的(絕對定位元素會脫離文件流)。如果一個沒有高度或者height是auto的容器的子元素是浮動元素,則該容器的高度是不會被撐開的。我們通常會利用偽元素(:after或者:before)來解決這個問題。BFC能包含浮動,也能解決容器高度不會被撐開的問題。

img

看個例子:

<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避免文字環繞

img

如上圖所示,對於浮動元素,可能會造成文字環繞的情況(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:

img

假設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; 
}
複製程式碼

現在儘管盒子的寬度稍有改變,但佈局不會打破。當然,對多列布局來說這不一定是個好辦法,但能避免最後一列下掉。這個問題上彈性盒或許是個更好的解決方案,但這個辦法可以用來說明元素在這些環境下的行為。

相關文章