高度塌陷與 BFC
1. 高度塌陷
在浮動布局中,父元素的高度默認是被子元素撐開的
當子元素浮動后,其會完全脫離文檔流,子元素從文檔流中脫離將會無法撐起父元素的高度,導致父元素的高度丟失
父元素高度丟失以后,其下的元素會自動上移,導致頁面的布局混亂
所以高度塌陷是浮動布局中比較常見的一個問題,這個問題我們必須要進行處理!
別急,我們接着往下看
2. BFC
BFC(Block Formatting Context)塊級格式化環境
- BFC 是一個 CSS 中的一個隱含的屬性,可以為一個元素開啟 BFC
- 開啟 BFC 該元素會變成一個獨立的布局區域
元素開啟 BFC 后的特點:
- 不會被浮動元素覆蓋
- 父子元素外邊距不會重疊
- 可以包含浮動的元素
可以通過一些特殊方式來開啟元素的 BFC:
-
設置為浮動(不推薦):很明顯下方元素被覆蓋了,總不能讓所有元素都浮動吧
-
設置為行內塊元素(不推薦):不再獨占一行,寬度變了,同時與下方元素產生了一點空隙
-
設置
overflow
為非visible
值:既沒有覆蓋元素,也保持了獨占一方的特性(保持了寬度),與下方元素也保持了最初的間隙常用的方式為元素設置
overflow:hidden
(overflow:auto
也是 ok 的) 開啟其 BFC, 以使其可以包含浮動元素overflow:scroll
會有滾動條,可能並不需要的,所以不太推薦不過,這種方式也存在一定問題,如下,
overflow
並沒有完全清除 div2 布局上受到的影響
總結
- 可以通過變成浮動元素,來防止自身被浮動元素覆蓋(有點“以毒攻毒”那味了)
- 可以設置行內塊,來防止自身及其他元素被浮動元素覆蓋(如果說浮動是“獨善其身”,那行內塊就有點“兼濟天下”的意思)
- 可以設置
overflow
屬性,包含浮動元素(既“獨善其身”,又“兼濟天下”,但仍有缺陷)
我們可以打開Zeal
手冊(《02-前端開發准備》有介紹),查看關於 BFC 的說明文檔
打開Block formatting context
模塊后,可以看到有很多開啟 BFC 的方式
我這里大概翻譯了一下,並整理了一份表格,應該看起來更直觀一點(有些概念因為還沒有學習,翻譯和理解有誤的地方還望諒解)
元素或屬性 | 說明 |
---|---|
<html> |
文檔根元素 |
float: left float: right |
浮動元素(float 不為none ) |
position: absolut position: fixed |
絕對定位元素 |
display: inline-block |
行內塊元素 |
display: table-cell |
表格單元,默認值 |
display: table-caption |
表格標題,默認值 |
display: table display: table-row display: table-row-group display: table-header-group display: table-footer-group display: inline-table |
匿名的表格單元,分別是 HTML 表格、表行、表體、表頭和表腳的默認值 |
overflow: hidden overflow: scroll overflow: auto |
overflow 不為visible 和clip 的塊元素 |
display: flow-root |
|
contain: layout contain: content contain: paint |
|
display: flex display: inline-flex 的直接子元素 |
Flex 項,如果它們本身既不是flex ,也不是grid 或table 容器 |
display: grid display: inline-grid 的直接子元素 |
Grid 項,如果它們本身既不是flex ,也不是grid 或table 容器 |
column-count 不為auto column-width 不為auto |
Multicol 容器,包含column-count: 1 |
column-span: all |
應該總是創建一個新的格式化上下文,即使column-span: all 元素不在 multicol 容器中 |
但是,注意不管哪種方式,多多少少都會有些隱患、缺陷或者說“副作用”
3. clear
我們這里設計三個兄弟元素,對前兩個元素進行float
的浮動屬性設置,看下效果
由於 box1 的浮動,導致 box3 位置上移也就是 box3 受到了 box1 浮動的影響,位置發生了改變(注意,這里文字並沒有被覆蓋,《09-浮動》一節說過浮動的特點,其中第 7 點就是“文字環繞”的問題)
如果我們不希望某個元素因為其他元素浮動的影響而改變位置,可以通過clear
屬性來清除浮動元素對當前元素所產生的影響
clear
作用:清除浮動元素對當前元素所產生的影響(本質是為元素添加一個margin-top
屬性,值由瀏覽器自動計算)
可選值:
left
清除左側浮動元素對當前元素的影響right
清除右側浮動元素對當前元素的影響both
清除兩側中影響較大一側元素的影響(注意,這里不是同時清除兩側的影響)
4. after
我們學習了上面知識后,了解了高度塌陷問題的解決方式,其中主要有
-
通過
overflow: hidden
等可以為元素開啟 BFC -
通過
clear: both
等可以清除浮動對元素產生的影響
同時也了解到,這兩種方式都有一定的弊端和隱患。那有沒有一種更好的方式去解決高度塌陷的問題呢?
答案當然是:有!
我們直接上效果圖
Q1:這里使用了一個偽元素選擇器::after
,那有人會問了,跟在 box2 下直接定義一個 box3 有什么區別呢?
A:我們知道,網頁的結構思想是:結構+表現+行為。在 box2 下直接定義一個 box3,屬於結構;而使用偽元素選擇器,屬於表現
而高度塌陷問題屬於表現問題,定義 box3 的目的是為了撐起 box1 的內容,屬於表現,而不是結構,所以在 css 中定義::after
更符合網頁的編程思想
Q2:為什么需要使用display: block
呢?
A:因為默認情況下,::after
偽元素是一個行內元素,如果不轉為塊元素,將仍然撐不起 box1 的高度
5. clearfix
我們在前面《06-盒模型》一節中說過垂直布局中邊距重疊的問題:相鄰的垂直方向外邊距會發生重疊現象
如上圖所示,子元素設置了一個margin-top
之后,父元素跟隨子元素一起進行了移動
即我們之前說的父子元素間相鄰外邊距,子元素會傳遞給父元素(上外邊距)
聰明的小伙伴已經想到了,用剛才說的偽元素選擇器啊
好,我們先來看下效果
貌似是沒有任何變化,到底是什么地方不對呢?
我們再來回顧下使用after
偽元素的心路歷程:
- 使用無內容的 box3 撐起 box1 ==》表現代替結構(
::after
代替 box3) clear
清除浮動對元素產生的影響(還記得clear
的原理么?)
其實就是給元素設置了一個margin-top
屬性,不過這個在開發者工具中是看不到的
既然如此,就相當於在 box2 下面添加一個 box3,然后給 box3 設置一個margin-top
屬性
到此為止,
∵ 相鄰的垂直方向外邊距
這個條件仍然滿足
∴ 會發生重疊現象
這個結論也依然成立
具體點就是,父子元素間相鄰外邊距,子元素會傳遞給父元素(上外邊距)
,表現為 box1 和 box2 同步往下移動
那我們應該怎么做才能解決這個問題? 憑你們朴素的情感,應該怎么判? 當然就是讓上述條件不滿足唄!
怎么能夠不滿足?當然是讓兩個元素垂直外邊距不相鄰啊!
好,多說無益,我們直接上代碼看效果!
我們用了before
偽元素選擇器,目的當然是讓 box1 和 box2 的外邊距不相鄰,但是好像並沒有效果
我們再換成display: inline-block
屬性看看
好像是解決了父元素布局的問題,但是子元素怎么還往下跑了一段距離? 是誰給的勇氣?
因為inline-block
兼顧行內元素和塊元素的特點,既可以設置寬高也不獨占一行
在沒有設置寬高時,會存在一個默認高度,所以inline-block
仍然行不通
還有一個屬性,display: table
Bingo!實現了我們最終想要的效果
Q1:為什么沒有使用 clear 屬性?
A:不是說了嗎?clear
是為了清除浮動對布局的影響,我們現在沒有浮動的元素啊,我們要討論的也不是浮動的問題
Q2:display 不是還有一個none
屬性么,為什么不用呢?
A:none
屬性是不占據位置,但是也不能讓元素相鄰的外邊距分離啊
Q3:為什么table
值就可以呢?
A:這個問題問的非常好,算是問到點上了!我們上面在講開啟 BFC 的一些方法的時候,也提到了該屬性。而且,應該牢記的是,元素開啟 BFC 后的其中一個特點就是 父子元素外邊距不會重疊
。當然,這里也需要合理選擇偽元素選擇器,使其外邊距不相鄰才行
另外,總結一下:
- 高度塌陷問題,一般用
::after
- 外邊距重疊問題,一般用
::before
不知道到這里,大家能不能想明白這兩件事情
那么問題來了,有沒有一個兩全其美的辦法,既可以解決高度塌陷,又可以解決外邊距重疊呢?
當然有!clearfix
這個樣式就可以同時解決高度塌陷和外邊距重疊的問題
當你在遇到這些問題時,直接使用clearfix
這個類即可,他就可以幫你輕松搞定 css 中的兩大難題
.clearfix::before,
.clearfix::after {
content: "";
display: table;
clear: both;
}
其中.clearfix::before
是為了解決外邊距重疊問題
.clearfix::before {
content: "";
display: table;
}
.clearfix::after
是為了解決高度塌陷問題
.clearfix::after {
content: "";
display: table;
clear: both;
}
兩者合在一起,就可以完美地解決高度塌陷和外邊距重疊這兩大“世紀難題”了