# [IM005] - BFC (Block Formatting Context)
> **前言**
> 2020 秋天,我將用 30 天的時間,來嘗試回答和網路前端開發相關的 30 個問題。30 天無法一網打盡浩瀚的前端知識,有些問題可能對有些讀者來說相對簡單,不過期待這趟旅程,能幫助自己、也幫助讀者打開不同的知識大門。有興趣的話,跟著我一起探索吧!
>
## BFC (Block Formatting Context)
在閱讀 CSS 相關文章時,應該偶爾會看到有人提到 BFC (Block Format Context),但其實過去從來也沒有認真搞懂過什麼是 BFC,就趁這個機會來深入探究一下吧!
## Definition(s)
首先,先來看看 [W3C]() 當中關於 "formatting context" 的解釋:
> A formatting context is the environment into which a set of related boxes are laid out. Different formatting contexts lay out their boxes according to different rules.
當我們在談到 "formatting context" 的時候,其實就在談論該環境下 "boxes" 將如何排列。所以常見的 formatting context 有
* block formatting context
* inline formatting context
* grid formatting context
* flex formatting context
如果有一點 HTML 和 CSS 的經驗的話,看到上面這些,應該很快就會猜想到他們代表著截然不同的排列方式。這裡我們不會深入介紹所有的 formatting context,只會來看最基本(可能也最為重要的)block formatting context.
接下來,我們來看看 [W3C](https://www.w3.org/TR/CSS2/visuren.html#normal-flow) 上關於 BFC 的說明:
> 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.
在 BFC 當中,boxes 會是垂直、一個接著一個的排列,兩個 boxes 之間的距離是由 margin 來決定,但是在垂直方向上的 margin,會有重疊的現象產生。
而 [MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context) 上的說明是:
> A block formatting context is a part of a visual CSS rendering of a web page. It's the region in which the layout of block boxes occurs and in which floats interact with other elements.
> Formatting contexts affect layout, but typically, we create a new block formatting context for the positioning and clearing floats rather than changing the layout, because an element that establishes a new block formatting context will:
>
> * contain internal floats.
> * exclude external floats.
> * suppress margin collapsing.
BFC 是當瀏覽器渲染網頁的時所建立出來的區域(環境),也就是 block boxes 佈局的區域,以及浮動元素與其他元素互動的區域。BFC 會影響到網頁的佈局,新建立的 BFC 將可以
* 包含內部浮動元素
* 排除(不管)外部浮動元素
* 解決 margin collapsing 的狀況
看到這裡,大概知道 BFC 會自動生成,但是後面提到三個新建 BFC 可以做到的事情又是什麼意思呢?
簡單來說,就是當我們刻意創造一個新的 BFC 出來之後,就可以改變原先的佈局方式,特別是與浮動元素的互動,以及 margin collapsing 的狀況。
接下來,就讓我們來看看三種行為的例子吧!
## [New BFC] 1. Contain internal floats
當一個元素新建立的 BFC 之後,其內部空間的大小(這裡特別指高度)將會包含浮動元素的高度。舉例來說,這裡有一個元素 `id="wrapper"`,當中有兩個元素, `id="one" ` 和 `id="two"`,其中因為 `id="one"` 為浮動元素,所以整個 `id=wrapper` 的大小會等同於 `id=two` 的大小。
```htmlmixed=
<!-- html -->
<div>
<div class="wrapper" id="wrapper">
<div class="float" id="one">Float Left</div>
<div class="box" id="two">Box</div>
</div>
</div>
```
```css
/* css */
.wrapper {
background-color: yellow;
border: 3px solid black;
}
.float {
float: left;
width: 200px;
height: 100px;
background-color: rgba(255, 255, 255, .5);
border:1px solid black;
padding: 10px;
}
```

不過如果我們在 `id="wrapper"` 當中新增 `overflow: auto` 的屬性,那麼他自己本身就會建立一個新的 BFC ,使得整個元素的高度,也會包含當中浮動元素的高度!
```css
/* css */
.wrapper {
background-color: yellow;
border: 3px solid black;
overflow: auto; /* 創造出新的 BFC */
}
```

## [New BFC] 2. Exclude external floats
當建立一個新的 BFC 之後,就會排除其外、原本需要和浮動元素互動的狀況。舉例來說,假設在一個 div 當中有兩個 boxes,其中一個有設定 float 如下:
```htmlmixed=
<!-- html -->
<div>
<div class="float" id="one">Float left</div>
<div class="box" id="two">Box</div>
</div>
```
```css
/* css */
.box {
background-color: yellow;
border: 3px solid black;
height: 50px;
}
.float {
float: left;
overflow: hidden;
resize: both;
margin-right: 25px;
width: 200px;
height: 100px;
background-color: rgba(255, 255, 255, .5);
border: 1px solid black;
padding: 10px;
}
```

<!--  -->
會發現 `id="one"` 的 box 會覆蓋在 `id="two"` box 之上。
如果我們在 `id="two"` 額外設定
```css
/* css */
#two { display: flow-root }
```

<!--  -->
就會發現兩個 boxes 就分開來不會重疊。這是因為我們幫 `id="two"` 的 box 建立了新的 BFC,離開了它原本所在的 BFC(需要考慮和浮動元素互動的 BFC),因此就會排除了和外部浮動元素的交互作用,也就沒有覆蓋的狀況發生。
## [New BFC] 3. Suppress margin collapsing
在本文的最一開始,有提到關於垂直方向 margin 重疊的現象。舉例來說,如果今天這裡有兩個 boxes 分別為
```htmlmixed=
<!-- html -->
<div class="box" id="one">one</div>
<div class="box" id="two">two</div>
```
同時我們也定義
```css
/* css */
.box { margin: 10px }
```
會發現上下兩個 boxes 實際的距離,是 10px 而不是 20px! 原因是這兩個 boxes 目前都在同一個 BFC 當中。若要想要排除這樣的狀況,我們可以刻意創造另外一個 BFC 如下
```htmlmixed=
<!-- html -->
<div class="box" id="one">one</div>
<!-- 創造出新的 BFC -->
<div class="wrapper">
<div class="box" id="two">two</div>
</div>
```
```css
/* css */
.box { margin: 10px }
.wrapper { overflow: hidden } /* 創造出新的 BFC */
```
就會發現兩個 boxes 之間的距離變寬了,即為 20px!
看完上面的三種行為,就會發現,當我們了解了 BFC 的行為後,其實就可以幫助我們快速調整佈局,避免不想要的狀況出現。
## How to create new BFC?
前面提到的 BFC 的特型,像是當中的 boxes 會如何排列,以及當我們刻意創造新的 BFC 之後,會產生什麼樣的改變。最後,就讓我們很快來看一下如何創造 BFC:
1. root element of the doc (`<html>`): root 本身就會建立 BFC。如果沒有子元素沒有建立自己的 BFC 或 IFC,那麼所有的子元素也都在同一個 BFC 當中。
2. float 不為 `none` 的元素
3. position 為 `absolute` 或 `fixed`
4. display 為 `inline-block`
6. display 為 `flow-root`
7. display 為 `flex` 或 `inline-flex`
8. display 為 `grid` 或 `inline-grid`
9. Block element 且 overflow 不為 `visible`
以上為較為常見的佈局方法,想知道其他可以創造新的 BFC 的佈局,可以參考 MDN 的說明。
## Ending
這篇文章簡單了介紹 BFC 以及使用上所帶來的影響,雖然有些現象過去已經知道,甚至很習慣了,不過多瞭解一點背後運作的道理,也許對於這些現象的印象就會更為深刻。
### Ref:
* [BFC (W3C)](https://www.w3.org/TR/css-display-3/#bfc)
* [Block formatting context (MDN)](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context)
* [Understanding CSS Layout And The Block Formatting Context](https://www.smashingmagazine.com/2017/12/understanding-css-layout-block-formatting-context/)
***
> **TD**
> 在北緯一度努力踏上軟體工程師之路的物理人,對世界運作的方式充滿好奇
> *"Life is like riding a bicycle. To keep your balance, you must keep moving."*
> [More about me](https://tsungtingdu.github.io/profile/)
***
###### tags: `ironman` `CSS`