BFC是什么?為啥要知道這個東西?今天我們來搞搞清楚。
首先,我們得弄清楚“視覺格式化模型”(visual formatting model)這個東西。要弄清楚這個,我們得先說說瀏覽器的渲染原理了。
http://taligarsiel.com/Projects/howbrowserswork1.htm
這是我很喜歡的一篇闡述瀏覽器運行原理的說明類文章,其中有一章提到了CSS的視覺模型(visual model)。根據其中的講述,瀏覽器是在畫板中繪制“格式化文檔結構”的,而在繪制的過程中,瀏覽器會為每一個元素節點都創建一個矩形空間并在其中放置文檔樹的內容,這也就是大名鼎鼎的css盒模型,而其放置元素的依據就是視覺格式化模型。
話說,這些被創建的盒子都有一個叫做display的屬性,來表明它到底是啥樣的盒子。也就是說,根據display的值,元素會對應不同類型的controlling box
。而針對不同類型的盒子,視覺格式化模型會反饋出不同的顯示機制。盒子的類型大概有:
塊盒(block box):
當一個盒子的display屬性被設置為block,list-item或 table時,會生成block-level box
行內盒(inline box):
當一個盒子的display屬性被設置為inline,inline-block或 inline-table,會生成inline-level box
匿名盒(anonymous box):
不能利用選擇器來選擇的盒子,所以它們的屬性為:inherit
或默認初始值
那么如上所述,BFC指的就是所謂的“塊盒”所參與到的塊級格式化過程中。同理可以類推,行內盒參與到的則是行內格式化上下文的過程,成為IFC。要注意,只有block-level box
才能參與到BFC的過程中,是只有哦。同樣,IFC也是同樣的道理。
關于FC,再多說兩句。每個盒子都有且僅有一個FC值,簡單理解,不同的FC值代表一組盒子不同的排列方式。BFC值表示盒子從上到下垂直排列,而IFC則是表示盒子從左到右的水平排列方式。inline-level box
的FC特性值固定為IFC。
每個BFC都有自己獨特的作用域范圍,并不互相影響。可以把它理解為一個容器,元素在其中按照自己的規則進行排列展示,但其他的BFC卻不感知。
如此,相信大家已經理解了BFC產生的原因了。那么,到底哪些條件會觸發BFC呢?整理一下從各方搜集來的資料:
- body根元素或其它包含它的元素;
- 浮動 (元素的float不為none,脫離文檔流);
- 絕對定位元素 (元素的position為absolute或fixed,脫離文檔流);
- 行內塊 inline-blocks(元素的 display: inline-block,具有block的特征);
- 表格單元格(元素的display: table-cell,HTML表格單元格默認屬性);
- overflow的值不為visible的元素 (hidden、auto、scroll);
- 彈性盒 flex boxes (元素的display: flex或inline-flex);
從這里可以看出,凡事具有塊級元素特征的盒子,或者是脫離文檔流的元素都屬于BFC,同時,我們熟悉的overflow屬性也可以創建BFC。
BFC具有自己獨特的布局規則。也整理一下:
- 內部的盒子會在垂直方向一個個地放置
- 屬于同一個BFC的兩個相鄰Box的上下margin會發生重疊,與方向無關
- 每個元素的左邊,與包含的盒子的左邊相接觸(對于從左往右的格式化,否則相反),即使存在浮動也是如此
- BFC的區域不會與float的元素區域重疊
- BFC就是頁面上的一個隔離的獨立容器,容器里面的子元素不會影響到外面元素,反之亦然
- 計算BFC的高度時,浮動元素也參與計算
好了,概念性的東西到此為止。到目前為止,我們理解了BFC是如何形成的、如何觸發的以及如何布局的,那么當遇到實際的布局問題的時候,我們同樣也可以根據BFC的原理來對元素布局進行調整,從而達到想要的布局效果。
首先舉一個最常見的相鄰Box上下margin重疊的例子吧:
<style>
.p {
width:200px;
height:50px;
margin:50px 0;
background-color:red;
}
</style>
<body>
<div class="p"></div>
<div class="p"></div>
</body>
我們可以分析一下上面的這個簡單例子。首先,body根元素觸發了BFC,所以其中包含的兩個元素P屬于該BFC下的相鄰Box,這時候對其賦值margin屬性,會命中相應的布局規則。因此,最終的布局效果是這樣的:
那么,如果要改進這種布局的話,只需要讓兩個div從屬于不同的BFC即可。實現的方式比較多:
方法一:
<div class="p"></div>
<div class="wrap">
<div class="p"></div>
</div>
.warp{
overflow: hidden;
}
.p{……}
方法二:
<div class="p1"></div>
<div class="p2"></div>
.p2{
float: left;
}
方法三:
<div class="p1"></div>
<div class="p2"></div>
.p2{
display: inline-block;
}
其實,本質上都是一樣的,給body的子元素創建一個BFC,從而避免margin的折疊。
再舉一個清除浮動覆蓋的例子。
<div>
<div class="aside"></div>
<div class="main"></div>
</div>
.aside {
width: 100px;
height: 150px;
float: left;
background: #f66;
}
.main {
height: 200px;
width: 200px;
background: #fcc;
}
上述代碼會導致浮動內容對主內容的覆蓋。
所以,如果對BFC有了了解,我們就可以得知,產生覆蓋的原因是,浮動的內容創建了新的BFC,導致其左邊與包含的盒子的左邊重合,從而導致了內容覆蓋。因此,清除覆蓋的辦法也很簡單,對其覆蓋元素創建另外的BFC就可以避免這個現象。
<div>
<div class="aside"></div>
<div class="main"></div>
</div>
.aside {
width: 100px;
height: 150px;
float: left;
background: #f66;
}
.main {
……
overflow: hidden; //其他任何觸發BFC的條件都可以
}
效果如下:
那么,為什么創建為新的BFC的main沒有也貼到包含盒子的左邊呢?當然是因為BFC區域不與浮動元素相重疊啦~
最后一個簡單的例子,利用BFC清除浮動
<div class="parent”>
overflow:hidden;
<div class="child"></div>
<div class="child"></div>
</div>
.parent {
border: 5px solid #fcc;
width: 300px;
}
.child {
border: 5px solid #f66;
width:100px;
height: 100px;
float: left;
}
如此,表象為:
所以,如果想讓內容把這個區域撐開,我們可以通過在parent元素上設置BFC來實現。
.parent {
overflow: hidden; //其他可以創建BFC的屬性均可
}
在這個例子中,值得注意的是,DOM中存在一個匿名盒子“overflow:hidden;”,而這個盒子的display屬性會繼承父元素的該屬性,所以,對parent元素的不同的BFC設置會影響到該行文字的顯示位置。比如:
.parent {
display: flex;
}
好了,就說這么多。有空我們再說說IFC,甚至是GFC、FFC吧!