iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 5
0
Modern Web

前端開發 30 個問題系列 第 5

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 上關於 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 上的說明是:

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 的大小。

<!-- 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 */
.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;
}

Imgur

不過如果我們在 id="wrapper" 當中新增 overflow: auto 的屬性,那麼他自己本身就會建立一個新的 BFC ,使得整個元素的高度,也會包含當中浮動元素的高度!

/* css */
.wrapper {
  background-color: yellow;
  border: 3px solid black;
  overflow: auto;              /* 創造出新的 BFC */
}

Imgur

[New BFC] 2. Exclude external floats

當建立一個新的 BFC 之後,就會排除其外、原本需要和浮動元素互動的狀況。舉例來說,假設在一個 div 當中有兩個 boxes,其中一個有設定 float 如下:

<!-- html -->
<div>
  <div class="float" id="one">Float left</div>
  <div class="box" id="two">Box</div>
</div>
/* 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; 
}

Imgur

會發現 id="one" 的 box 會覆蓋在 id="two" box 之上。

如果我們在 id="two" 額外設定

/* css */
#two { display: flow-root }

Imgur

就會發現兩個 boxes 就分開來不會重疊。這是因為我們幫 id="two" 的 box 建立了新的 BFC,離開了它原本所在的 BFC(需要考慮和浮動元素互動的 BFC),因此就會排除了和外部浮動元素的交互作用,也就沒有覆蓋的狀況發生。

[New BFC] 3. Suppress margin collapsing

在本文的最一開始,有提到關於垂直方向 margin 重疊的現象。舉例來說,如果今天這裡有兩個 boxes 分別為

<!-- html -->
<div class="box" id="one">one</div>
<div class="box" id="two">two</div>

同時我們也定義

/* css */
.box { margin: 10px }

會發現上下兩個 boxes 實際的距離,是 10px 而不是 20px! 原因是這兩個 boxes 目前都在同一個 BFC 當中。若要想要排除這樣的狀況,我們可以刻意創造另外一個 BFC 如下

<!-- html -->
<div class="box" id="one">one</div>
<!-- 創造出新的 BFC -->
<div class="wrapper">                
  <div class="box" id="two">two</div>
</div>

/* 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 為 absolutefixed
  4. display 為 inline-block
  5. display 為 flow-root
  6. display 為 flexinline-flex
  7. display 為 gridinline-grid
  8. Block element 且 overflow 不為 visible

以上為較為常見的佈局方法,想知道其他可以創造新的 BFC 的佈局,可以參考 MDN 的說明。

Ending

這篇文章簡單了介紹 BFC 以及使用上所帶來的影響,雖然有些現象過去已經知道,甚至很習慣了,不過多瞭解一點背後運作的道理,也許對於這些現象的印象就會更為深刻。

Ref:


TD
Be curious as astronomer, think as physicist, hack as engineer,
fight as baseball player
More about me

"Life is like riding a bicycle. To keep your balance, you must keep moving."



上一篇
More about CSS display
下一篇
z-index and stacking context
系列文
前端開發 30 個問題31

尚未有邦友留言

立即登入留言