(注1:如果有問題歡迎留言探討,一起學習!轉載請注明出處,喜歡可以點個贊哦!)
(注2:更多內容請查看我的目錄。)
1. BFC定義
BFC的英文全稱是:Block Formatting Contexts,直譯為“塊格式化上下文”。
BFC是Web頁面的可視化CSS渲染的部分,是塊級盒布局發生的區域,也是浮動元素與其他元素交互的區域。
2. BFC生成
CSS2.2文檔中注明,滿足下列條件之一,會為其內容建立新的塊格式化上下文BFC:
- 浮動,即float的值不為none。
- 絕對定位的元素,position的值為absolute或fixed
- 不是塊盒的塊容器。諸如行內塊inline-blocks,表單元格table-cells和表標題table-captions。 即 display的值為table-cell, table-caption, inline-block中的任何一個。
- 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 notnone
)- absolutely positioned elements (elements where
position
isabsolute
orfixed
)- 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), orinline-table
- block elements where
overflow
has a value other thanvisible
display
: [flow-root](https://drafts.csswg.org/css-display/#valdef-display-flow-root)
- elements with
contain
: layout
,content
, orstrict
- flex items (direct children of the element with
display
: flex
orinline-flex
)- grid items (direct children of the element with
display
: grid
orinline-grid
)- multicol containers (elements where
column-count
orcolumn-width
is notauto
, including elements withcolumn-count: 1
)column-span
: all
should always create a new formatting context, even when thecolumn-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>
如圖,三個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>
可以看到,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>
可以看到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>
我們發現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>
可以看到,如果設置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>
可以看到,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.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>
這里需要注意的是,如果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 神奇背后的原理