CSS中重要的BFC

SHERlocked93發表於2018-07-20

CSS中有個重要的概念BFC,搞懂BFC可以讓我們理解CSS中某些原本詭異(??)的地方。

1. 簡介

在解釋BFC之前,先說一下文件流。我們常說的文件流其實分為定位流浮動流普通流三種。而普通流其實就是指BFC中的FC。FC(Formatting Context),直譯過來是格式化上下文,它是頁面中的一塊渲染區域,有一套渲染規則,決定了其子元素如何佈局,以及和其他元素之間的關係和作用。常見的FC有BFC、IFC,還有GFC和FFC。

BFC(Block Formatting Context)塊級格式化上下文,是用於佈局塊級盒子的一塊渲染區域。MDN上的解釋:BFC是Web頁面 CSS 視覺渲染的一部分,用於決定塊盒子的佈局及浮動相互影響範圍的一個區域。

注意:一個BFC的範圍包含建立該上下文元素的所有子元素,但不包括建立了新BFC的子元素的內部元素。這從另一方角度說明,一個元素不能同時存在於兩個BFC中。因為如果一個元素能夠同時處於兩個BFC中,那麼就意味著這個元素能與兩個BFC中的元素髮生作用,就違反了BFC的隔離作用。

2. 三種文件流的定位方案

常規流(Normal flow)

  • 在常規流中,盒一個接著一個排列;
  • 在塊級格式化上下文裡面, 它們豎著排列;
  • 在行內格式化上下文裡面, 它們橫著排列;
  • 當position為static或relative,並且float為none時會觸發常規流;
  • 對於靜態定位(static positioning),position: static,盒的位置是常規流佈局裡的位置;
  • 對於相對定位(relative positioning),position: relative,盒偏移位置由top、bottom、left、right屬性定義。即使有偏移,仍然保留原有的位置,其它常規流不能佔用這個位置。

浮動(Floats)

  • 左浮動元素儘量靠左、靠上,右浮動同理
  • 這導致常規流環繞在它的周邊,除非設定 clear 屬性
  • 浮動元素不會影響塊級元素的佈局
  • 但浮動元素會影響行內元素的佈局,讓其圍繞在自己周圍,撐大父級元素,從而間接影響塊級元素佈局
  • 最高點不會超過當前行的最高點、它前面的浮動元素的最高點
  • 不超過它的包含塊,除非元素本身已經比包含塊更寬
  • 行內元素出現在左浮動元素的右邊和右浮動元素的左邊,左浮動元素的左邊和右浮動元素的右邊是不會擺放浮動元素的

絕對定位(Absolute positioning)

  • 絕對定位方案,盒從常規流中被移除,不影響常規流的佈局;
  • 它的定位相對於它的包含塊,相關CSS屬性:top、bottom、left、right;
  • 如果元素的屬性position為absolute或fixed,它是絕對定位元素;
  • 對於position: absolute,元素定位將相對於上級元素中最近的一個relative、fixed、absolute,如果沒有則相對於body;

3. BFC觸發方式

  1. 根元素,即HTML標籤
  2. 浮動元素:float值為leftright
  3. overflow值不為 visible,為 autoscrollhidden
  4. display值為 inline-blocktable-celltable-captiontableinline-tableflexinline-flexgridinline-grid
  5. 定位元素:position值為 absolutefixed

注意 display:table也可以生成BFC的原因在於Table會預設生成一個匿名的table-cell,是這個匿名的table-cell生成了BFC。

4. 約束規則

瀏覽器對BFC區域的約束規則:

  1. 生成BFC元素的子元素會一個接一個的放置。
  2. 垂直方向上他們的起點是一個包含塊的頂部,兩個相鄰子元素之間的垂直距離取決於元素的margin特性。在BFC中相鄰的塊級元素的外邊距會摺疊(Mastering margin collapsing)
  3. 生成BFC元素的子元素中,每一個子元素左外邊距與包含塊的左邊界相接觸(對於從右到左的格式化,右外邊距接觸右邊界),即使浮動元素也是如此(儘管子元素的內容區域會由於浮動而壓縮),除非這個子元素也建立了一個新的BFC(如它自身也是一個浮動元素)。

規則解讀:

  1. 內部的Box會在垂直方向上一個接一個的放置
  2. 內部的Box垂直方向上的距離由margin決定。(完整的說法是:屬於同一個BFC的兩個相鄰Box的margin會發生摺疊,不同BFC不會發生摺疊。)
  3. 每個元素的左外邊距與包含塊的左邊界相接觸(從左向右),即使浮動元素也是如此。(這說明BFC中子元素不會超出他的包含塊,而position為absolute的元素可以超出他的包含塊邊界)
  4. BFC的區域不會與float的元素區域重疊
  5. 計算BFC的高度時,浮動子元素也參與計算

5. 作用

BFC是頁面上的一個隔離的獨立容器,容器裡面的子元素不會影響到外面元素,反之亦然。我們可以利用BFC的這個特性來做很多事。

5.1 阻止元素被浮動元素覆蓋

一個正常文件流的block元素可能被一個float元素覆蓋,擠佔正常文件流,因此可以設定一個元素的float、display、position值等方式觸發BFC,以阻止被浮動盒子覆蓋。

使用BFC阻止元素被浮動元素覆蓋

5.2 可以包含浮動元素

通過改變包含浮動子元素的父盒子的屬性值,觸發BFC,以此來包含子元素的浮動盒子。

使用BFC包含浮動元素

注意,這裡觸發BFC並不能阻止其它形式的脫離文件流的元素覆蓋正常流元素。

BFC內部其他形式脫離文件流(absolute fixed)

5.3 阻止因為瀏覽器因為四捨五入造成的多列布局換行的情況

有時候因為多列布局採用小數點位的width導致因為瀏覽器因為四捨五入造成的換行的情況,可以在最後一列觸發BFC的形式來阻止換行的發生。比如下面栗子的特殊情況

使用BFC阻止多列布局最後一列換行

5.4 阻止相鄰元素的margin合併

屬於同一個BFC的兩個相鄰塊級子元素的上下margin會發生重疊,(設定writing-mode:tb-rl時,水平margin會發生重疊)。所以當兩個相鄰塊級子元素分屬於不同的BFC時可以阻止margin重疊。 這裡給任一個相鄰塊級盒子的外面包一個div,通過改變此div的屬性使兩個原盒子分屬於兩個不同的BFC,以此來阻止margin重疊。

使用BFC阻止margin合併

但是這裡有個疑問: 如果外面包一層div,設定能觸發BFC的任何屬性都能阻止相鄰元素的margin合併。因為分屬不同BFC不會發生margin合併。 而如果在外面不包一個div的話,當設定display為inline-block、inline-flex、table-captain,和position為absolute、fixed,float為left、right是可以阻止margin合併的。這裡問題來了:

我們知道設定position和float會讓元素脫離文件流並且又建立新的BFC,所以兩個元素就不是相鄰元素了,因此可以阻止相鄰元素margin合併,但是inline-block、inline-flex、inline-grid、table-captain為什麼可以呢?如果有人知道為什麼,請告知~


網上的帖子大多深淺不一,甚至有些前後矛盾,在下的文章都是學習過程中的總結,如果發現錯誤,歡迎留言指出~

參考:

  1. 我對BFC的理解
  2. 深入理解BFC和Margin Collapse
  3. 深入理解BFC
  4. Understanding Block Formatting Contexts in CSS
  5. 學習BFC
  6. Understanding Block Formatting Contexts in CSS
  7. 帶你徹底掌握 CSS 浮動

PS:歡迎大家關注我的公眾號【前端下午茶】,一起加油吧~

CSS中重要的BFC

相關文章