說起BFC,就必須先了解一下CSS文件流中的定位機制,而且這部分說簡單也簡單,但卻有個坑有可能誤導我們,特在本文作出解釋。
一、文件流中的定位機制
1. 三種基本方式
CSS有三種基本定位機制,我想大多數朋友都掌握了,在此簡要概述一下(不浪費時間):
- 普通流(或稱常規流):CSS預設的定位方式,觸發方式包括為position: static/relative,且float:none
- 浮動:浮動脫離普通流,可以左右移動,直到它的外邊框邊緣碰到包含框或另一個浮動框的邊緣,觸發方式基本就是float:left/top等。
- 絕對定位:盒子脫離普通流,不影響普通流上其他元素的佈局
2. 注意事項
所以,此時大部分初學者都會結合現實世界感覺到這三種流的立體感就是如下圖所示的這樣:
這裡我想說的是,上圖這種表達方式不能說是完全正確,雖然層級明確了,但這會給初學者帶來錯誤的理解:浮動元素既然已經玩飄了,且普通流中的div也不會再給它留地方了,那浮動元素對普通應該就徹底沒影響了吧? 當然不是! 直接上個小例子看看(多餘上色之類的程式碼去掉了):
<body>
<div class='parent'>
<div class='child1'>
Child1
</div>
<div class='child2'>
冷咖啡離開了杯墊
我忍住的情緒在很後面
</div>
</div>
</body>
<style>
.parent {
width:400px;
min-height:100px;
}
.child1 {
width:50px;
height:50px;
}
.child2 {
width:100px;
height:100px;
}
</style>
複製程式碼
效果如下:
好的,那接下來,按照我們的想法,把child1設為浮動,此時它就應該脫離普通流,且普通流中的元素不再考慮它的位置了,child1新增float:left後如下:
咦!child1確實感覺浮動了,child2也確實毫不客氣的把child1原本的位置給頂替了, 可child2裡面的元素是咋回事?為什麼像躲摔倒的老太太一樣躲避child1了呢? 這就是我想提醒朋友們一定要注意的地方。下面看看解釋:
float起初被設計出來的初衷其實是為了實現報紙上的那種文字環繞圖片的效果,就像我們上例這種普通流中的文字環繞在child1的周圍。但之後大家才發現結合float + div 可以實現一定的網頁佈局,所以,這是浮動的特性。即浮動只是脫離了文件流,即不會按照你的設計而進行佈局,就算它在任何位置上那也是和文件流中的其他內容是是平級關係的,我們在使用浮動時,基本上就考慮它的佈局定位就行了,不要覺得它徹底飄了,它的影響還在。
好了,這個坑幫不知道的朋友解決了,接下來就好理解BFC了。
二、BFC探索之路
你可能在不知道BFC是啥玩意的前提下卻知道解決子div浮動而造成父div塌陷的一個經典的辦法就是給父div設定overflow:hidden,但要是面試官問你為啥,你可能就歇菜了,別問我怎麼知道,我曾經就是那個菜雞...,所以,瞭解了BFC,你就知道為啥了。
1. BFC概念
BFC(Block Formatting Context,塊格式上下文)是Web頁面的視覺化CSS渲染的一部分,並且有自身的一套渲染規則,它決定了其子元素如何定位,以及和其他元素的關係和相互作用。
2. BFC特點
具有BFC特性的元素可以看成是隔離了的獨立容器,容器裡面的元素不會在佈局上影響到外面的元素,並且BFC具有普通容器所沒有的一些特性。很多好的文章羅列的內容有:
- 內部的box會在垂直方向,從頂部開始一個接一個地放置;
- box垂直方向的距離由margin決定,屬於同一個BFC的兩個相鄰的box的垂直方向的margin會發生疊加;
- 在BFC中,每個盒子的左外邊緣(margin-left)會觸碰到容器的左邊緣(border-left)。對於從右到左的格式來說,則觸碰到右邊緣,即使是浮動的也是如此,即不會發生margin穿透;
- 形成了BFC的區域不會與float box重疊;
- 計算BFC高度時,子浮動元素也參與計算(BFC會確切包含浮動的子元素,即閉合浮動);
所以,再遇到下面這些問題時,就可以回答了:
- 如何保證一個div元素不被同級的浮動元素覆蓋:設定該div元素為BFC;
- 為什麼父div設定overflow:hidden就可以在其子div為浮動的情況下依然可以撐開:因為此時父div是個BFC。
但是!這裡我還想說一下我的一些想法,即:我認為,之所以提出BFC的概念,其主要目的是為了隔離出獨立容器,而容器本身與其他元素之間是否完全滿足上述特性,這應該區別對待,我們應該把BFC的核心思維用在它本身內部而非外部,後面文章會有例子。
3. 觸發BFC的條件
有朋友羅列了好多,記住一些常規的即可:
- 根元素或包含根元素的元素,這裡應該就是body元素;
- 浮動元素(float不是none);
- 絕對定位元素(position:absolute/fixed);
- 行內塊元素(display:inline-block);
- 表格單元格(display:table-cell ,html表格單元格預設為該值);
- 表格標題(display:table-caption , html表格標題預設為該值);
- 匿名錶格單元格元素(display:table / table-row / table-row-group / table-header-group / table-footer-group / inline-table ,分別是html table 、row、tbody、thead、tfoot的預設屬性);
- overflow:非visible 的塊元素;
- display:flow-root;
- contain:layout / content / strict;
- 彈性元素(display:flex / inline-flex 元素的直接子元素);
- 網格元素(display:grid / inline-grid 元素的直接子元素);
- 多列容器(元素的column-count 或 column-width 不為 auto,且包括 column-count為1);
- column-span 為all 的元素始終會建立一個新的BFC,即使該元素沒有報過在一個多列容器中;
所以,每次看到 overflow:hidden、float:left/right 、position:absolute/fixed ,不用懷疑,一定是BFC
4. 實際使用場景
BFC在實際開釋出局中基本可以做這兩件事:
- 避免由於子元素浮動造成父元素塌陷;
- 避免同一個BFC中的兩個塊級元素的垂直方向margin重疊(設定其中一個為一個新的BFC,永久隔絕外部影響);
- 自適應兩欄佈局。
5. BFC例項
直接看看吧
<body>
<div class='box'>
<div class='left'>左邊</div>
<div class='right'>右邊
<div class='little'>1</div>
<div class='little'>2</div>
<div class='little'>3</div>
</div>
</div>
</body>
<style>
.box {
background:#888;
overflow:hidden;
margin-left:50px;
}
.left {
background: #73DE80; /* 綠色 */
width:200px;
height:200px;
}
.right {
background: #EF5BE2; /* 粉色 */
width:400px;
min-height:100px;
}
.little {
background: #fff;
width: 50px;
height: 50px;
margin: 10px;
float:left;
}
</style>
複製程式碼
效果如下:
如果突然把div left元素 新增一個float:left:
看到了吧!div right元素的子元素騰地方了,那接下來把div right變成一個BFC,比如使用:overflow:hidden,效果如下:
這樣,就滿足了BFC內部元素不受外界干擾,且不會與float元素重疊兩個特性。
但是,如果我給box right 元素設定了絕對定位,那它是會覆蓋box left這個浮動元素的,可這就不符合BFC的不會與float元素重疊這個特性了:
所以我提出了我和我同事討論出的想法,即我們不能把上述羅列的BFC特性徹底肯定,因為這裡涉及到的是兩個大的知識層。當設定了絕對定位,雖然也觸發了BFC,但是它更是直接影響了層級關係,所以優先實現後者。
以上就是我想提醒廣大和我一樣的初級朋友,歡迎大家批評指正,謝謝您!!!~~~
參考文獻
- 《學習 BFC》 juejin.im/post/59b73d…
- 《關於BFC理解》 reng99.cc/2018/08/12/…