理解 BFC (Block Formatting Model)

什麼是 BFC

W3C 爲瀏覽器規定了三種定位模型:Normal Flow, 浮動, 和絕對定位。本文所介紹的 BFC (Block Formatting Model) 是屬於 Normal Flow 中的一小節內容,而且這部份內容只有三段話。第一段:css

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.html

簡單來講,BFC 僅僅是一種定位的狀況,即當元素知足 BFC 的條件時,瀏覽器怎麼去顯示它。上面這短話講的是如何創建 BFC,只要知足其中任何一個條件就能夠了。可能須要解釋一句話,就是:block containers that are not block boxes。瀏覽器

block box 是指塊級元素,就是會強制換行的元素,好比 div。block container 是指能夠包含塊級元素的元素,但它不必定要換行,好比 div, td。能夠看到,後者的範圍更大一點,二者異或的結果就是這句話的意思了。除了規範上列舉的 inline-block、table-cell 等,主流瀏覽器還支持 flex 。less

創建 BFC

默認的 div 是塊級元素,並非 BFC,因此須要一些額外的樣式來生成。按照規範,咱們大概有以下(不止)的手段,以及相應的代價:佈局

  • disblay: table; 沒法解決響應式的問題。flex

  • display: inline-block; 會使後續內聯元素顯示在同一行。spa

  • position: absolute; 將元素從 normal flow 中剝離出來。code

  • float: left; 將元素推向左側。orm

  • overflow: scroll; 會出現滾動條。htm

  • overflow: hidden; 會截斷溢出的內容。

請根據不一樣的實際狀況,選擇最合適的方式。

仔細想一下,在 normal flow 的章節中寫道,用 float: left; 或者 position: absolute; 去建立 BFC,是否是有點矛盾?否則。BFC 指的是爲子元素建立一個定位環境,浮動和絕對定位是針對該元素自己,而不是其子元素。

外邊距合併

在 normal flow 中,咱們知道,垂直方向上相鄰元素的外邊距取二者中較大的值,例子:

<div class="container">
  <div class="c1">第一塊</div>
  <div class="c1">第二塊</div>
  <div class="c1">第三塊</div>
</div>

相應的 CSS:

.container {margin:20px;border:1px solid #212121;width:300px;background-color:#009688;}
.c1 {margin:10px 0;height:40px;background-color:#FFC107;}

結果示意圖:

邊距合併

能夠看到,每一個黃色塊之間的距離是 10px,而不是相加的 20px 。擴展一下,若是用 .c1 {margin:10px 0 15px;} 那麼黃色塊之間的距離就是 15px,取較大值。若是是 .c1 {margin:10px 0 -15px;} 呢?是 10px,仍是 0px,仍是 -5px?

關於邊距合併,規範中是這麼描述的:

Vertical margins between adjacent block-level boxes in a block formatting context collapse.

也就是說,邊距合併的條件是,子元素處於相同的 BFC 中,若是爲其中一個子元素建立新的 BFC,就不會發生合併的狀況了。

<div class="extra">
    <div class="c1">第三塊</div>
</div>

如今爲第三塊添加一個額外的父元素,而且爲該元素建立 BFC:

.extra {overflow:hidden;}

圖不單獨給了,這裏給個問題,就是,若是不添加父元素,直接把 .extra 類加給第三個塊,以下:

<div class="c1">第一塊</div>
<div class="c1">第二塊</div>
<div class="c1 extra">第三塊</div>

能夠防止邊距合併嗎?不行。再次重申一下,BFC 是爲元素建立定位環境。

BFC 的應用

清除浮動

若是一個塊級元素的全部子元素都浮動了,那麼它的高度就爲 0 。

.c1 {float:left;margin:10px;width:100px;height:50px;background-color:#FFC107;}

對於一樣的 html,修改 .c1的樣式,添加浮動的效果,能夠看到

浮動

父元素只剩下了邊框,並無 content height 。若是要清除浮動,能夠爲容器添加僞類:

.container:after{display:block;content:'';clear:both;}

這樣,父元素的高度就又回來了。

經過 clear 清除浮動

固然,咱們也能夠爲父元素添加 overflow:hidden; 來清除浮動。

.container {overflow:hidden;}

這兩個方法均可行,前者很直白,clear:both; 原本就是用於清除浮動的,爲何用 overflow:hidden 也能夠?由於這樣就建立了一個 BFC,對於子元素的排列,規範是這麼寫的:

In a block formatting context, each box's left outer edge touches the left edge of the containing block. This is true even in the presence of floats, unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).

子元素的外邊緣會觸碰到父元素的左側(嚴格來講是,子元素外邊距和父元素的內邊距)。若是父元素的高度爲 0,那麼就違背規範了。

因此爲父元素添加浮動、絕對定位等任何用於建立 BFC 的方式,均可以清除浮動。

防止文字環繞

常見的一種佈局就是,特別是評論回覆中,會看到在頭像右側有一段文字,就算文字很長,也不會出如今頭像下方,會一直保持在右側。

非文字環繞

這樣的 HTML 格式很簡單:

<div class="container">
  <div class="avatar"></div>
  <div class="comment">...</div>
</div>

.comment 中的內容省略不寫了。若是僅僅對 .avatar 左浮,就會出現文字環繞現象:

文字環繞

解決的辦法能夠是爲 .comment 添加一個適當的左外邊距,好比 90px 。

另外一個辦法是利用 BFC 。

根據規範,子元素的外邊距必須向左靠着父元素的內邊距,也就是說,儘管 .avatar 浮動了,可是 .comment 仍是會靠着父元素的左側內邊距,它們二者是上下重疊的!

unless the box establishes a new block formatting context,這句話代表,只要爲 .comment 建立 BFC,那麼這個約束就能夠被打破,即它們不會重疊。

in which case the box itself may become narrower due to the floats,因爲浮動元素佔據了必定的寬度,新建立的 BFC 會所以而變窄。

.avatar {width:70px;height:70px;float:left;margin:10px;background-color:#ffc107;}
.comment {overflow:hidden;}

多列布局

通常來講,若是要作寬度自適應的多列布局,就會用百分比作寬度單位,而且子元素浮動。那麼問題就是,若是百分比換算到像素時,一旦子元素的寬度之和大於父元素,最後一列就會換行。

<div class="container">
  <div class="column"></div>
  <div class="column"></div>
  <div class="column"></div>
</div>

CSS3 自己能夠實現多列布局,用 columns 屬性或者 flex 佈局。這裏只是爲了更好地去理解 BFC 而作一個例子。

.column {float:left;margin:0 1%;width:31.33%;height:100px;background-color:#ffc107;}

先來算一下,左右邊距各 1%,寬度 31.33%,三列布局,總共 99.99%,應該不會超。但對於部分瀏覽器,寬度換算時是四捨五入的,好比,父元素的寬度爲 500px,那麼四捨五入,每列寬度爲 157px,左右邊距 5px,總和 (157 + 5 2) 3 = 501,正好超 1px,最後一列換行。

最後一列要換行是由於浮動元素的特性,若是它不是浮動元素,那麼就能夠按照 normal flow 的規則來,即,向左靠至父元素左邊距。

而後,根據上個小節的知識,若是爲元素建立新的 BFC,那麼「向左靠」的規則能夠打破。兩個條件結合,獲得:

.column:last-child {float:none;overflow:hidden;}

多列布局

雖然能夠防止換行,可是最後一列的間距有點不一致。

本文主要是講了 BFC 的兩個關鍵點,第一:如何建立,以及每種方式的代價;第二:特性,防止默認的邊距合併和向左靠至父元素內側。不少衍生用法都是基於這兩種特性來的。

不過,每每咱們是直接去記用 overflow:hidden; 能夠清除浮動,並無想爲何,畢竟內容很少。有時候想一想原理,也挺有意思的。

相關文章
相關標籤/搜索