CSS進階04-塊格式化上下文BFC

(注1:如果有問題歡迎留言探討,一起學習!轉載請注明出處,喜歡可以點個贊哦!)
(注2:更多內容請查看我的目錄。)

1. BFC定義

BFC的英文全稱是:Block Formatting Contexts,直譯為“塊格式化上下文”。
BFC是Web頁面的可視化CSS渲染的部分,是塊級盒布局發生的區域,也是浮動元素與其他元素交互的區域。

2. BFC生成

CSS2.2文檔中注明,滿足下列條件之一,會為其內容建立新的塊格式化上下文BFC:

  1. 浮動,即float的值不為none。
  2. 絕對定位的元素,position的值為absolute或fixed
  3. 不是塊盒的塊容器。諸如行內塊inline-blocks,表單元格table-cells和表標題table-captions。 即 display的值為table-cell, table-caption, inline-block中的任何一個。
  4. overflow值不為visible的塊盒。(除非該值被傳播到視口viewport)

然后,MDN中注明,如下條件會產生BFC(這里我直接放英文原版):

A block formatting context is created by one of the following:

  • the root element or something that contains it
  • floats (elements where float is not none)
  • absolutely positioned elements (elements where position is absolute or fixed)
  • inline-blocks (elements with display: inline-block)
  • table cells (elements with display: table-cell, which is the default for HTML table cells)
  • table captions (elements with display: table-caption, which is the default for HTML table captions)
  • anonymous table cells implicitly created by the elements with display: table, table-row, table-row-group, table-header-group, table-footer-group (which is the default for HTML tables, table rows, table bodies, table headers and table footers, respectively), or inline-table
  • block elements where overflow has a value other than visible
  • display: [flow-root](https://drafts.csswg.org/css-display/#valdef-display-flow-root)
  • elements with contain: layout, content, or strict
  • flex items (direct children of the element with display: flex or inline-flex)
  • grid items (direct children of the element with display: grid or inline-grid)
  • multicol containers (elements where column-count or column-width is not auto, including elements with column-count: 1)
  • column-span: all should always create a new formatting context, even when the column-span: all element isn't contained by a multicol container (Spec change, Chrome bug).

發現新增了很多情況。這里只討論CSS2.2中所列條件,當然CSS2.2中root默認也會生成BFC(不過我一直沒找到文檔中的出處,如果有找到的同學還請不吝賜教),但是body默認是不生成BFC的。

3. BFC渲染規則

CSS2.2中規定BFC具有如下特征:

  • 在一個塊格式化上下文中,盒從包含塊頂部開始一個接一個地垂直擺放。兩個同胞盒間的垂直距離取決于 margin 屬性。同一個塊格式化上下文中的相鄰塊級盒的垂直外邊距將折疊。
  • 在一個塊格式化上下文中,每個盒的左外邊緣緊貼包含塊的左邊緣(從右到左的格式里,則為盒右外邊緣緊貼包含塊右邊緣),即使有浮動參與也是如此(盡管盒里的行盒可能由于浮動而收縮),除非盒創建了一個新的塊格式化上下文(在這種情況下盒子本身可能由于浮動而變窄)。

下面,我們詳細舉例說明這兩條規則:

3.1 在一個塊格式化上下文中,盒從包含塊頂部開始一個接一個地垂直擺放。兩個同胞盒間的垂直距離取決于 margin 屬性。同一個塊格式化上下文中的相鄰塊級盒的垂直外邊距將折疊。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>3.1-1</title>
    <style>
        div {
            background-color: green;
            height: 30px;
            margin: 15px;
        }
    </style>
</head>
<body>
<div>div1</div>
<div>div2</div>
<div>div3</div>
</body>
</html>
3.1-1

如圖,三個div生成的塊盒在root生成的BFC下,其包含塊是body的content box(就是body的content edge生成的塊),三個div塊從body的content box頂部往下依次排列。三者間被margin隔開。但是由于三者處于同一個BFC,margin產生了折疊(折疊的情況比較復雜,我會開單章詳細說明),間距不是30px而是15px。

3.2在一個塊格式化上下文中,每個盒的左外邊緣緊貼包含塊的左邊緣(從右到左的格式里,則為盒右外邊緣緊貼包含塊右邊緣),即使有浮動參與也是如此(盡管盒里的行盒可能由于浮動而收縮),除非盒創建了一個新的塊格式化上下文(在這種情況下盒子本身可能由于浮動而變窄)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>3.2-1</title>
    <style>
        div {
            background-color: green;
            height: 30px;
            margin: 15px;
        }
        .fl {
            float: left;
        }
        .div4 {
            background-color: red;
            height: 20px;
        }
    </style>
</head>
<body>
<div id="div1">div1</div>
<div id="div2">div2</div>
<div class="fl div4">float-left</div>
<div id="div3">div3</div>
</body>
</html>
3.2-1

可以看到,id為div1,div2和div3的三個div生成的塊盒在root生成的BFC下,每個盒的左外邊緣緊貼其包含塊(body的content box)的左邊緣。內容為float-left的div雖然把div3內容向右擠開了一段距離(原因是浮動導致id為div3的盒內的行盒收縮),但是id為div3的盒的左外邊緣仍然緊貼其包含塊的左邊緣。

不過值得注意的是此時內容為float-left的塊盒由于浮動脫離了標準流,此時不再與div2的margin發生折疊,所以對于3.1的折疊發生條件,應該至少還要加一個前提,就是相鄰塊級盒需要在標準流內。

另外,我們看看,如果此時使id為div3的盒生成新的BFC呢?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>3.2-2</title>
    <style>
        div {
            background-color: green;
            height: 30px;
            margin: 15px;
        }
        .fl {
            float: left;
        }
        .div4 {
            background-color: red;
            height: 20px;
        }
    </style>
</head>
<body>
<div id="div1">div1</div>
<div id="div2">div2</div>
<div class="fl div4">float-left</div>
<div id="div3" class="fl">div3</div>
</body>
</html>
3.2-2

可以看到div3由于浮動生成了新的BFC,會導致盒的左外邊緣不再緊貼其包含塊的左邊緣,并且由于浮動,其本身寬度變窄了。

4. BFC用途

4.1 BFC可以阻止元素被浮動元素覆蓋(防止高度坍塌)。

盒的寬高其實是有著很復雜的計算方法,這一點我們在CSS進階系列后面的文章中詳細說明。這里我們來看一個例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>4.1-1</title>
    <style>
        .div1 {
            height: 50px;
            background-color: gray;
        }
        .outer {
            border: 3px solid red;
        }
        .fl {
            float: left;
        }
    </style>
</head>
<body>
<div class="outer">
    outer
    <div class="div1 fl">float-left</div>
</div>
</body>
</html>
4.1-1

我們發現outer的盒高度竟然沒有其所包含的float-left的盒高度高,產生了高度塌陷。這是什么原因呢?這是因為對于

Block-level non-replaced elements in normal flow when 'overflow' computes to 'visible'

的高度,有

Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored, and relatively positioned boxes are considered without their offset).

就是說對于標準流中的塊級非替換元素,如果其overflow最終計算結果為visible,那么高度只會考慮其在標準流中的子元素(比如,移動和絕對定位的盒子是會被忽略的,相對定位的盒子只會考慮其未被定位前的位置)。

那么,BFC如何清除浮動呢?看下面這個例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>4.1-2</title>
    <style>
        .div1 {
            height: 50px;
            background-color: gray;
        }
        .outer {
            border: 3px solid red;
        }
        .fl {
            float: left;
        }
    </style>
</head>
<body>
<div class="outer fl">
    outer
    <div class="div1 fl">float-left</div>
</div>
</body>
</html>
4.1-2

可以看到,如果設置outer的float屬性不為none,會導致outer生成新的BFC,然后outer盒的在高度上能夠包裹住float-left了。這是什么原理呢?

the height of an element that establishes a block formatting context is computed as follows:

If it only has inline-level children, the height is the distance between the top of the topmost line box and the bottom of the bottommost line box.

If it has block-level children, the height is the distance between the top margin-edge of the topmost block-level child box and the bottom margin-edge of the bottommost block-level child box.

Absolutely positioned children are ignored, and relatively positioned boxes are considered without their offset. Note that the child box may be an anonymous block box.

In addition, if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge, then the height is increased to include those edges. Only floats that participate in this block formatting context are taken into account, e.g., floats inside absolutely positioned descendants or other floats are not.

這短話詳細列舉了生成BFC元素的高度計算規則,翻譯成中文如下:

建立塊格式化上下文的元素的高度按如下所述計算:

如果該元素只有行內級子元素,其高度為最上行盒的頂部到最下行盒的底部的距離。

如果該元素有塊級子元素,其高度為最上塊級子盒的上外邊距邊緣到最下塊級子盒的下外邊距邊緣的距離。

絕對定位子元素會被忽略,相對定位盒不需要考慮其位移。注意子盒可能是匿名塊盒。

此外,如果該元素有下外邊距邊緣低于該元素下內容邊緣的浮動子元素,那么高度將增大來包含那些邊緣。只有參與本塊格式化上下文的浮動才考慮在內,比如,在絕對定位后代中的或者其他浮動中的浮動就不考慮。

4.2 BFC可以用來防止margin折疊。

看下面這個例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>4.2</title>
    <style>
        .div1 {
            margin: 10px;
            height: 30px;
            background-color: green;
        }
        .div2 {
            margin: 10px;
            height: 30px;
            background-color: blue;
        }
        .hidden {
            overflow: hidden;
        }
    </style>
</head>
<body>
<div class="div1">div1</div>
<div class="outer">
    <div class="div2">div2</div>
</div>
<div class="div1">div1</div>
<div class="outer hidden">
    <div class="div2">div2</div>
</div>
</body>
</html>
4.2

可以看到,div2即使包含在另一個塊盒中,但是如果與div1在同一個BFC且相鄰,一樣會產生margin折疊。這時如果讓div2處于一個新的BFC下,則其與處于另一個BFC下的div1不再會有margin折疊。

4.3 多欄布局(更多內容可以參考BFC與多列布局)

4.3.1 兩欄布局

特點:側邊欄寬度固定,內容欄可以根據瀏覽器寬度自適應。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>4.3.1</title>
    <style>
        body {
            width: 100%;
        }

        .aside {
            width: 100px;
            height: 150px;
            float: left;
            background: grey;
        }

        .content {
            height: 200px;
            overflow:hidden;
            background: blue;
        }
    </style>
</head>
<body>
<div class="aside"></div>
<div class="content"></div>
</body>
</html>
4.3.1
4.3.2 三欄布局

特點:兩側寬度固定,中間內容欄可以根據瀏覽器寬度自適應。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>4.3.2</title>
    <style>
        body {
            width: 100%;
        }
        .left {
            width: 100px;
            height: 150px;
            float: left;
            background: grey;
        }

        .content {
            height: 200px;
            overflow:hidden;
            background: blue;
        }
        .right {
            width: 100px;
            height: 150px;
            float: right;
            background: purple;
        }
    </style>
</head>
<body>
<div class="left"></div>
<div class="right"></div>
<div class="content"></div>
</body>
</html>
4.3.2

這里需要注意的是,如果body部分寫成如下順序

<body>
<div class="left"></div>
<div class="content"></div>
<div class="right"></div>
</body>

那么,右邊欄會被擠到下邊。這是因為當“非float的元素”和“float的元素”在一起的時候,如果非float元素在先,則按照bfc規則,下一個盒子會換行,那么float的元素生成的盒子會在新的一行進行浮動。所以要將right放在content元素前面。

參考

https://www.w3.org/TR/CSS22/visuren.html#visual-model-intro
https://www.w3.org/TR/CSS2/visuren.html
https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context
CSS規范 > 9 視覺格式化模型 Visual Formatting Model
CSS規范 > 10 視覺格式化模型詳述 Visual Formatting Model Details
CSS > 譯文:理解CSS中的塊級格式化上下文
[譯]:BFC與IFC
css3中的BFC,IFC,GFC和FFC
深入理解BFC和Margin Collapse
我對BFC的理解
深入理解BFC
css3之BFC、IFC、GFC和FFC
前端精選文摘:BFC 神奇背后的原理
BFC與多列布局
前端精選文摘:BFC 神奇背后的原理

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,776評論 1 92
  • 先前在學習CSS float時,有同學提到了BFC這個詞,作為求知好問的好學生,哪里不懂查哪里,那么今天就來研究一...
    這名字真不對閱讀 6,569評論 3 19
  • 1.浮動元素有什么特征?對父容器、其他浮動元素、普通元素、文字分別有什么影響? 何謂浮動元素?有什么特征?所謂浮動...
    草鞋弟閱讀 824評論 0 1
  • 什么是BFC BFC全稱是Block Formatting Context,即塊格式化上下文。它是CSS2.1規范...
    陌客百里閱讀 536評論 3 4
  • 轉載自(http://web.jobbole.com/83274/) BFC BFC全稱是Block Format...
    居客俠閱讀 2,148評論 0 20