在上文《詳說清除浮動》中,Kayo 較為詳細地介紹了 BFC ,也就是本文的主角 Block Formatting Contexts (塊級格式化上下文),本文會基於上文關於 BFC 的部分進行拓展,進一步說明 BFC 的特性。

但在進一步說明 BFC 特性之前,Kayo 首先要介紹另一個在 CSS 的可視化格式模型 (Visual Formatting Model) 中具有非常重要地位的概念——定位方案。定位方案是控制元素的布局,在 CSS 2.1 中,有三種定位方案——普通流 (Normal Flow) 、浮動 (Floats) 和絕對定位 (Absolute Positioning) ,下面分別對這三種布局簡略說明一下。

普通流(Normal Flow)

在普通流中,元素按照其在 HTML 中的先后位置至上而下布局,在這個過程中,行內元素水平排列,直到當行被占滿然后換行,塊級元素則會被渲染為完整的一個新行, 除非另外指定,否則所有元素默認都是普通流定位,也可以說,普通流中元素的位置由該元素在 HTML 文檔中的位置決定。

浮動 (Floats)

在浮動布局中,元素首先按照普通流的位置出現,然后根據浮動的方向盡可能的向左邊或右邊偏移,其效果與印刷排版中的文本環繞相似。

絕對定位 (Absolute Positioning)

在絕對定位布局中,元素會整體脫離普通流,因此絕對定位元素不會對其兄弟元素造成影響(如果看了上文的童鞋,會發現這點與浮動元素會影響兄弟元素是不同的),而元素具體的位置由絕對定位的坐標決定。

BFC 正是屬於普通流的,因此它對兄弟元素也不會造成什么影響。

一. BFC 是什么?

有了上面的基礎后,可以正式介紹 BFC 了。從樣式上看,具有 BFC 的元素與普通的容器沒有什么區別,但是從功能上,具有 BFC 的元素可以看作是隔離了的獨立容器,容器里面的元素不會在布局上影響到外面的元素,並且 BFC 具有普通容器沒有的一些特性,例如可以包含浮動元素,上文中的第二類清除浮動的方法(如 overflow 方法)就是觸發了浮動元素的父元素的 BFC ,使到它可以包含浮動元素,從而防止出現高度塌陷的問題。

簡單來說,BFC 就是一種屬性,這種屬性會影響着元素的定位以及與其兄弟元素之間的相互作用。

二.如何觸發 BFC

上面介紹了 BFC 的定義,那么如何觸發 BFC 呢?

滿足下面任一條件的元素,會觸發為 BFC :

  • 浮動元素,float 除 none 以外的值
  • 絕對定位元素,position(absolute,fixed)
  • display 為以下其中之一的值 inline-blocks,table-cells,table-captions
  • overflow 除了 visible 以外的值(hidden,auto,scroll)

但是,"display:table" 本身並不產生 BFC,而是由它產生匿名框,匿名框中包含 "display:table-cell" 的框會產 BFC。 總之,對於 "display:table" 的元素,產生 BFC 的是匿名框而不是 "display:table"。

在 CSS3 中,BFC 叫做 Flow Root,並增加了一些觸發條件:

  • display 的 table-caption 值
  • position 的 fixed 值,其實 fixed 是 absolute 的一個子類,因此在 CSS2.1 中使用這個值也會觸發 BFC ,只是在 CSS3 中更加明確了這一點。

值得注意的是,在前面 Kayo 已經說明過了,BFC 並不是元素,而是某些元素帶有的一些屬性,因此,是上面這些元素產生了 BFC ,而它們本身並不是 BFC ,這個概念需要區分清楚。

三. BFC 的特性

從整體上看,BFC 是隔離了的容器,這個具體可以表現為三個特性:

1. BFC 會阻止外邊距折疊

兩個相連的 div 在垂直上的外邊距會發生疊加,有些書籍會把這個情況列作 bug ,這里 Kayo 並不同意,這種折疊雖然會給不熟悉 CSS 布局的開發者帶來一些不便,但實際上它具有完整且具體的折疊規則,並且在主流瀏覽器中都存在,因此 Kayo 更認為這應該是 CSS 的特性。當然,在實際開發中,或許我們有時會不需要這種折疊,這時可以利用 BFC 的其中一個特性——阻止外邊距疊加。

在舉例說明 BFC 如何阻止外邊距折疊之前,首先說明一下外邊距折疊的規則:僅當兩個塊級元素相鄰並且在同一個塊級格式化上下文時,它們垂直方向之間的外邊距才會疊加。也就是說,即便兩個塊級元素相鄰,但當它們不在同一個塊級格式化上下文時它們的邊距也不會折疊。因此,阻止外邊距折疊只需產生新的 BFC 。

效果如圖:

也可以看 Demo 。

如上圖的例子,三個 div 各包含一個 p 元素,三個 div 及其包含的 p 元素都有頂部和底部的外邊距,但只有第三個 div 的邊距沒有與它的子元素 p 的外邊距折疊。這是因為第三個 div 創建了新的 BFC ,由此可見:創建了 BFC 的元素,不和它的子元素發生外邊距折疊。

2. BFC 可以包含浮動的元素

這也正是使用 overflow: hidden 與 overflow: auto 方法閉合浮動的原理,使用 overflow: hidden 或 overflow: auto 觸發浮動元素的父元素的 BFC 特性,從而可以包含浮動元素,閉合浮動。

W3C 的原文是“'Auto' heights for block formatting context roots”,也就是 BFC 會根據子元素的情況自動適應高度,即使其子元素中包括浮動元素。

效果如圖:

也可以看 Demo 。

上面的例子中,有兩個 div ,它們各包含一個設置了浮動的 p 元素,但第一個 div 出現了“高度塌陷”,這是因為內部的浮動元素脫離了普通流,因此該 div 相當於一個空標簽,沒有高度和寬度,即高度為 0 ,上下邊框也重疊在一起。而第二個 div 使用 overflow: hidden 觸發了 BFC ,可以包含浮動元素,因此能正確表現出高度,其邊框位置也正常了。

3. BFC 可以阻止元素被浮動元素覆蓋

如上文所說,浮動元素的塊級兄弟元素會無視浮動元素的位置,盡量占滿一整行,這樣就會被浮動元素覆蓋,為該兄弟元素觸發 BFC 后可以阻止這種情況的發生。

效果如圖:

也可以看 Demo 。

如上圖的例子,藍色背景的 div 使用 overflow: hidden 觸發了 BFC ,它並不會被它的兄弟浮動元素覆蓋,而是處於它的旁邊。值得注意的是,以上的情況僅僅是元素寬度之和沒有超出父元素寬度的情況,假設浮動元素寬度和它的非浮動兄弟元素寬度都沒有超過父元素寬度,但兩個元素的寬度加起來超出了父元素寬度的時候,非浮動元素會下降到下一行,即處於浮動元素下方,效果如下圖:

也可以看 Demo 。

四. BFC 與 hasLayout

細心的童鞋會發現,在上面的例子中,除了使用 overflow: hidden 觸發 BFC 外,還使用了一個 *zomm: 1 的屬性,這是 IEhack ,因為 IE6-7 並不支持 W3C 的 BFC ,而是使用私有屬性 hasLayout 。從表現上來說,hasLayout 跟 BFC 很相似,只是 hasLayout 自身存在很多問題,導致了 IE6-7 中一系列的 bug 。觸發 hasLayout 的條件與觸發 BFC 有些相似,具體情況 Kayo 會另寫文章介紹。這里 Kayo 推薦為元素設置 IE 特有的 CSS 屬性 zoom: 1 觸發 hasLayout ,zoom 用於設置或檢索元素的縮放比例,值為“1”即使用元素的實際尺寸,使用 zoom: 1 既可以觸發 hasLayout 又不會對元素造成其他影響,相對來說會更為方便。

這時我們需要注意一個問題:既然 hasLayout 有着跟 BFC 相似的功能,那么在實際開發中,就要為需要觸發 BFC 的元素同時觸發 hasLayout ,這樣 BFC 和 hasLayout 具有的一些特殊性質可以在現代瀏覽器和 IE 中同時產生,避免一個元素在不同瀏覽器間的表現因為 BFC 或 hasLayout 出現差異。事實上,在實際開發中很多莫名其妙的問題其實都是因此而產生的。當然同樣地,如果一個元素沒有觸發 BFC ,也要盡量保證它沒有觸發 hasLayout 。

本文由 Kayo Lee 發表,本文鏈接:http://kayosite.com/block-formatting-contexts-in-detail.html