CSS朝花夕拾之塊級格式上下文BFC

塊級格式上下文(blocking format context,縮寫爲BFC)是個老生常談的話題,前端面試時十有八九會問到。以前並不瞭解它,後來看了一些文章稍微理解了,但是隨後就忘了,所以最近又重新看這個東西,把它記錄在博客裏加深記憶。先附上參考的資料,以表示對其作者的敬意。
1. 前端精選文摘:BFC 神奇背後的原理
2. 什麼是BFC
3. CSS2 BFC模型和IFC模型
4. 格式化上下文( Formatting context )

這幾篇文章很值得拜讀,本文章也只是稍微總結一下,加深記憶。

一、 什麼是BFC

首先給出W3C組織對BFC的定義,你也可以點擊這查看原文

9.4 Normal flow
Boxes in the normal flow belong to a formatting context, which may be block or inline, but not both simultaneously. Block-level boxes participate in a block formatting context. Inline-level boxes participate in an inline formatting context.

9.4.1 Block formatting contexts

Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with ‘overflow’ other than ‘visible’ (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.

In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the ‘margin’ properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.

In a block formatting context, each box’s left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box’s line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).

我簡要解釋一下這段話的意思,解釋不當之處還請指正:

第一段說明塊級盒子參與塊級格式上下文BFC。

第二段解釋了哪些元素可以建立新的BFC,它們是浮動元素、絕對定位元素、本身不是塊盒子的塊級元素如inline-blocks、table-cells、table-captions,還有overflow屬性值不爲visible的塊盒子。

第三段說明了BFC的佈局:盒子在包含塊的垂直方向上一個接一個放置,兄弟盒子的垂直距離由其margin屬性決定。兩個相鄰盒子的垂直方向的margin值會摺疊。

第四段講在BFC中,每個盒子的左外邊距挨着包含塊的左邊界(對於從右向左的格式來說)。即使在有浮動元素的存在下,這也是成立的,除非盒子建立了一個新的塊級格式上下文(在有些情況下,盒子本身可能會因爲浮動元素的存在而變窄)。

關於塊級元素,這也有說明

Block-level elements are those elements of the source document that are formatted visually as blocks (e.g., paragraphs). The following values of the ‘display’ property make an element block-level: ‘block’, ‘list-item’, and ‘table’.

Block-level boxes are boxes that participate in a block formatting context. Each block-level element generates a principal block-level box that contains descendant boxes and generated content and is also the box involved in any positioning scheme. Some block-level elements may generate additional boxes in addition to the principal box: ‘list-item’ elements. These additional boxes are placed with respect to the principal box.

Except for table boxes, which are described in a later chapter, and replaced elements, a block-level box is also a block container box. A block container box either contains only block-level boxes or establishes an inline formatting context and thus contains only inline-level boxes. Not all block container boxes are block-level boxes: non-replaced inline blocks and non-replaced table cells are block containers but not block-level boxes. Block-level boxes that are also block containers are called block boxes.

塊級元素是那些可視化爲塊的元素(如p元素),display值爲block、list-item、table可以創建塊級元素。塊級元素參與塊級格式上下文,每個塊級元素生成一個包含後代盒子和生成內容的主要塊級盒,且這個盒子參與了任何定位運算。一些盒子除了生成主要盒子外還可能生成其他盒子,如list-item元素。這些額外盒子關於主盒子排列。除了表格和可置換元素,塊級盒子也是塊容器盒子。塊容器盒子要麼只包含塊級盒子要麼建立行級格式上下文。並不是所有的塊容器盒子都是塊級盒子,如非置換行內塊和非置換表單元格都是塊容器卻不是塊級盒子。那些也是塊容器的塊級盒子被叫做塊盒子。

由此可以得到:BFC(Block formatting context)是一個獨立的渲染區域,只有Block-level Box參與, 它規定了內部的Block-level Box如何佈局,並且與這個區域外部毫不相干。

  1. BFC佈局規則
    • 內部的Box會在垂直方向,一個接一個地放置。
    • Box垂直方向的距離由margin決定。屬於同一個BFC的兩個相鄰Box的margin會發生重疊
    • 每個元素的margin box的左邊, 與包含塊border box的左邊相接觸(對於從左往右的格式化,否則相反)。即使存在浮動也是如此。
    • BFC的區域不會與float box重疊。
    • BFC就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。反之也如此。
    • 計算BFC的高度時,浮動元素也參與計算
  2. 生成BFC的元素
    • 根元素
    • float屬性不爲none
    • position爲absolute或fixed
    • display爲inline-block, table-cell, table-caption, flex, inline-flex
    • overflow不爲visible

二、 BFC的應用

  1. 兩欄佈局(左邊固定寬度,右邊自適應)

    <div class="container">
      <aside class="aside"></aside>
      <main class="main"></main>
    </div>
    *{
      padding:0;
      margin:0;
    }
    .aside{
      width:200px;
      height:400px;
      background-color:#fefe88;
      float:left;
    }
    .main{
      height:500px;
      background-color:#88fefe;
    }

    示意圖
    圖中可以看出.main和.aside區域重疊了,按照浮動元素的佈局來說,.main的文字會圍繞浮動元素,我們想要兩欄佈局,而這不是我們想要的佈局。爲什麼會這樣呢?這是因爲BFC佈局規則的第三條:每個元素的margin box的左邊, 與包含塊border box的左邊相接觸,即使存在浮動也是如此。所以.main左區域會緊挨.container的左側。
    爲此我們可以利用佈局規則的第四條:BFC的區域不會與float box重疊。爲.main新建一個BFC。可以爲做如下設置:

    .main{
      height:500px;
      background-color:#88fefe;
      overflow:auto;
    }

    得到如下所示:
    爲.main新建bfc

  2. 解決浮動導致的父元素坍塌
    考慮如下佈局:

    <div class="container">
      <div class="left"></div>
      <div class="right"></div>
    </div>
    *{
      padding:0;
      margin:0;
    }
    .left{
      width:200px;
      height:400px;
      background-color:#fefe88;
      float:left;
    }
    .right{
      width:200px;
      height:400px;
      background-color:#88fefe;
      float:left;
    }

    我們知道父元素如果僅包含浮動元素,且沒有設置寬高時,其寬會爲窗口的寬度,高度爲0,這也就是父元素高度坍塌。其原因是浮動元素脫離了標準流。上個例子中雖然.aside脫離文檔流,但.main沒有脫離,所以.container還是有高度的。那怎麼解決呢?可以利用佈局規則第六條:計算BFC的高度時,浮動元素也參與計算。所以可以將.container變爲bfc,同樣可以採用overflow屬性。

    .container{overflow:auto;}
  3. 解決外邊距摺疊
    試看如下代碼:
<div class="test">設置下外邊距30px</div>
<div class="test">設置上外邊距30px</div>
.test{
      width:200px;
      height:100px;
      background-color:#fe88fe;
      margin:50px 0;
    }

佈局圖如下所示:
示意圖
從上圖可以看出兩個塊的外邊距發生了摺疊,原本我們希望兩個塊間隔達到50px+50px的外邊距,卻得到了只有50px的間隔。爲什麼呢?佈局規則第二條:- Box垂直方向的距離由margin決定。屬於同一個BFC的兩個相鄰Box的margin會發生重疊。注意是同一個BFC的兩個相鄰box margin發生重疊。你可能會問就上面的兩行html,沒看出哪建立了bfc呀!原因是他們都處於根元素下,而根元素會產生BFC。解決方法可以根據佈局規則第五條:BFC就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。反之也如此。所以可以爲二者其中一個建立一個新的bfc,因爲bfc是隔離獨立的,不會影響外面的佈局,外面也不會影響它自己的佈局。就不會發生外邊距摺疊問題了。如下所示:

<div class="test">設置下外邊距50px</div>
<div class="wrap">
  <div class="test">設置上外邊距50px</div>
</div>
.test{
  width:200px;
  height:100px;
  background-color:#fe88fe;
  margin:50px 0;
}
.wrap{
  overflow:hidden;
}

外邊距摺疊問題解決了
好了,這樣問題就解決了。
其實寫這篇文章挺心虛的。一方面我並沒有仔細對W3C文檔做深入理解,另一方面只是對看過的幾篇文章做個簡單地總結,設置有些還是照搬原文的。而自己的東西還是少之又少的。就這樣吧!
如您發現錯誤,請不吝指正。

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