理解BFC
更好閱讀體驗移步:http://zhangzippo.github.io/posts/2019/04/28/_28理解bfc.html
BFC(格式化上下文)在整個css體系中是比較重要的概念,也牽扯了很多其他問題比如如浮動、定位、盒模型等,這篇文章主要幫助理解bfc的概念,特性和應用場景。
概念
先看一段來自MDN的定義:
塊格式化上下文(Block Formatting Context,BFC) 是Web頁面的可視化CSS渲染的一部分,是塊盒子的布局過程發(fā)生的區(qū)域,也是浮動元素與其他元素交互的區(qū)域。
嗯,非常嚴謹且官方(就是看不懂)那么我嘗試用一種便于理解的方式來解釋這個BFC,你可以簡單的理解BFC就是頁面上的一塊兒區(qū)域,這個區(qū)域有一些特性,并且該區(qū)域內(nèi)部有一套自己的規(guī)則不對外界元素產(chǎn)生干擾,是一塊相對獨立的渲染區(qū)域。(注意,參與BFC的是塊盒子)
參考其他博客中我認為最貼切的解釋:
塊格式上下文是頁面CSS 視覺渲染的一部分,用于決定塊盒子的布局及浮動相互影響范圍的一個區(qū)域。
產(chǎn)生這塊區(qū)域需要滿足以下幾種條件:
- 根元素或包含根元素的元素
- 浮動元素(元素的
float不是none)- 絕對定位元素(元素的
position為absolute或fixed)- 行內(nèi)塊元素(元素的
display為inline-block)overflow值不為visible的塊元素display值為flow-root的元素- 表格單元格(元素的
display為table-cell,HTML表格單元格默認為該值)- 表格標題(元素的
display為table-caption,HTML表格標題默認為該值)- 匿名表格單元格元素(元素的
display為table、table-row、table-row-group、table-header-group、table-footer-group(分別是HTML table、row、tbody、thead、tfoot的默認屬性)或inline-table)contain值為layout、content或strict的元素- 彈性元素(
display為flex或inline-flex元素的直接子元素)- 網(wǎng)格元素(
display為grid或inline-grid元素的直接子元素)- 多列容器(元素的
column-count不為auto,包括column-count為1)column-span為all的元素始終會創(chuàng)建一個新的BFC,即使該元素沒有包裹在一個多列容器中。
可以看出有很多觸發(fā)BFC區(qū)域的條件,其中前幾個斜粗體的描述是我們經(jīng)常遇到也需要重點記住的,后面的幾種可以當作擴展了解就好。
這里重點說一下display:flow-root,display:flow-root是什么意思呢?它可以創(chuàng)建無副作用的BFC。在父級塊中使用 display: flow-root 可以創(chuàng)建新的BFC。我們剛才分析了上面幾種創(chuàng)造bfc的方式,但他們都是有副作用的(比如float會讓元素脫離文檔流靠左或靠右,position的絕對定位也會脫離文檔流,overflow的值也會造成一些額外的影響),而這個值的設定是單純創(chuàng)造一個BFC,沒有任何其他影響,但是這個屬性目前瀏覽器支持的不是很好,下圖是這個值的兼容情況:

影響的范圍
這里引用MDN的解釋:
塊格式化上下文包含創(chuàng)建它的元素內(nèi)部的所有內(nèi)容,但不包括創(chuàng)建了新BFC的子元素的內(nèi)部元素。
塊格式化上下文對浮動定位與清除浮動都很重要。浮動定位和清除浮動時只會應用于同一個BFC內(nèi)的元素。浮動不會影響其它BFC中元素的布局,而清除浮動只能清除同一BFC中在它前面的元素的浮動。外邊距折疊也只會發(fā)生在屬于同一BFC的塊級元素之間。
這段話的重點在于1:BFC將影響內(nèi)部包含的所有內(nèi)容,除非內(nèi)部創(chuàng)建了新的子BFC區(qū)域;2:所有的BFC影響只會發(fā)生在同級次的BFC區(qū)域中。
如下面的代碼,box創(chuàng)建了一個BFC區(qū)域,那么將影響son_1,son_1-1,son_2,由于son_自己創(chuàng)建了一個BFC區(qū)域,因此son_2-1將不受box的BFC影響,而只受son_2的BFC影響
<div id='box' class='BFC'>
<div id='son_1'>
<div id='son_1-1'></div>
</div>
<div id='son_2' class='BFC'>
<div id='son_2-1'></div>
</div>
</div>
從下圖我門可以看到,外層盒子創(chuàng)造了一塊兒BFC區(qū)域會清除浮動,第一層因為浮動的關系撐開了外層盒子,由于float:left創(chuàng)造了一塊兒新的BFC區(qū)域,因此它的子元素的float不會撐開最外層,因此沒有受到最外層BFC的影響(沒有撐開第二層因為第二層給了固定高度)。這更佳說明了BFC是一塊獨立區(qū)域,并且內(nèi)部不會影響B(tài)FC外部元素的特性。

特性
關于以下特性介紹在css2.1規(guī)范的文檔中(文章結尾文獻)有相關介紹,這里前幾條是翻譯過來講,后面幾條是來自其他文章的總結。
特性1:在BFC中,內(nèi)部塊(block塊級元素)從頂部開始依次垂直排列。兩個同級塊之間的垂直距離由“Margin”屬性確定。
這個特性沒什么說的,就是在BFC區(qū)域內(nèi)部的子塊級元素會垂直排列,這點即使不在BFC內(nèi)其實也是一樣的,多個div會在容器內(nèi)部垂直的一個一個排列。
特性2: 內(nèi)部塊垂直方向的距離由margin決定。屬于同一個BFC的兩個相鄰Box的margin會發(fā)生重疊。
這個特性是指相鄰的兩個子塊元素的相隔距離由margin決定,但并不是margin的和,而是會重疊(實際就是是margin之間的最大值),如下代碼:
<div class="box">
<div class="son">
I am one
</div>
<div class="son">
I am two
</div>
<div class="son">
I am three
</div>
</div>
<style>
.box {
background-color: rgb(224, 206, 247);
display:flow-root; // 創(chuàng)造一個BFC區(qū)域
width:100%;
}
.son {
width: 200px;
height: 150px;
background-color: white;
border: 1px solid black;
padding: 10px;
margin:10px 0;
}
.son:last-child{
margin:20px 0;
}
</style>
效果如下:

可以看到每個元素之間的margin是10像素,而最后一個box與上方的box之間是20,因為margin的重疊導致了取的是兩個相鄰box的最大值。
特性3:每個子塊元素的左外邊緣接觸包含塊的左邊緣(對于從右到左的格式,右邊緣接觸)。即使在存在浮動的情況下也是如此。
這里解釋一下,我們知道每一個box模型是由四個層級構成的,如圖:

從外到內(nèi)分別是margin-box,border-box,padding-box和content-box,這個規(guī)則的意思就是說我們的子塊的外邊緣(也就是margin-box邊緣)會與BFC區(qū)域的邊緣(也就是content-box的邊緣),即使在子塊存在浮動的情況下也是一樣的。
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
display:flow-root;
padding: 10px;
}
.float {
float:left; // 添加浮動
width: 200px;
height: 150px;
background-color: white;
border: 1px solid black;
margin:0 10px;
}
內(nèi)部元素有一個10像素的margin,外部盒子有10像素的padding,內(nèi)部元素的margin-box的左邊與包含塊content-box的做邊緣接觸

特性4:BFC就是頁面上的一個隔離的獨立容器,容器里面的子元素不會影響到外面的元素,反之亦然;
這點在上面我們介紹影響范圍的時候介紹過,不再做過多說明了。
特性5:計算BFC的高度時,考慮BFC所包含的所有元素,連浮動元素也參與計算
這個特性就是為何子元素浮動,如果要清除浮動可以在父元素上創(chuàng)造一個bfc的解釋,這樣高度就囊括了浮動元素。
特性6:浮動盒區(qū)域不疊加到BFC上
<div class="box"></div>
<div class="float"></div>
<style>
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
height:100px;
width:100px;
padding:10px; display:flow-root;
}
.float {
float:left;
height: 150px;
background-color: white;
border: 1px solid black;
padding: 10px;
}
</style>
可以看到浮動元素沒有覆蓋到bfc上面

應用
1. 分屬于不同的BFC時可以阻止margin重疊
根據(jù)特性2我們知道,BFC內(nèi)的元素排列時margin會重疊,因此我們?yōu)榱朔乐怪丿B,可以使兩個元素創(chuàng)建自己的bfc,這樣margin就不會重疊。
2. 可以包含浮動元素——清除內(nèi)部浮動
根據(jù)特性五可以得知。
3. 可以阻止元素被浮動元素覆蓋
根據(jù)特性六,只需要將被覆蓋的元素創(chuàng)建成為BFC就可以解決這個問題。
4. 自適應兩欄布局
我們可以利用bfc的特性來構造兩欄布局:
<div class="left"></div>
<div class="right"></div>
<style>
.left{
background:yellow;
width:200px;
height:400px;
float:left;
}
.right{
background:pink;
height:400px;
/*添加overflow:hidden,觸發(fā)元素BFC*/
overflow:hidden;
}
</style>

當然我們也可以利用該特性進行3欄的布局,道理都是一樣的。
這篇文章對BFC的前置概念沒有過多說明,比如盒模型以及定位方案等,可以查看https://juejin.im/post/59b73d5bf265da064618731d這篇文章,本篇文章也部分參考了該文章。
參考文獻
https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Block_formatting_context
https://www.w3.org/TR/CSS21/visuren.html#block-formatting
https://juejin.im/post/59b73d5bf265da064618731d