用 clearfix 解決 Bootstrap grid system 跑版問題,以及其背後原理
文前備註:本文當時是以 Bootstrap 3 作討論,與現在最新的 Bootstrap 4 在語法上略有差異,不過機制上相同,文章最後將會列出文內提及程式碼在兩版本中差異。
問題描述:
在使用 Bootstrap 3過程中,發現 grid system 雖然能夠快速排版,但如果 div
內的物件大小不一時,會使 div
長短不同造成跑版。例如下圖是以 row
包起來的頁面,原本預期輸出3x3卡片式頁面,結果因為圖片長短不一導致跑版。
解決方法:clearfix
解決方法是在每個區間下加入下列程式碼(❶) :
<div class="clearfix hidden-xs"></div>
本例是 col-md-4
,也就是三個為一排,所以每三個卡片下要加入 clearfix
。下面這段重複三次就能達成原本在 md
大小畫面上預期輸出3x3卡片式頁面。
<div class="col-md-4">卡片1的內容</div>
<div class="col-md-4">卡片2的內容</div>
<div class="col-md-4">卡片3的內容</div>
<div class=”clearfix hidden-xs”></div>
用 Chrome 的 DevTools 會發現,只是插一行空的 clearfix
而已,竟然就把三個卡片給包起來了! 到底怎麼做到的?
跑版的罪魁禍首: float
在問 clearfix
怎麼做到之前,要先探討"為什麼會發生這個問題?"。如果用 DevTools 點開 col-md-4
,會發現其實每張卡片都是以 float: left
進行排版。也因為 float
的特性,才會有這個問題。
當橫向沒有空間的時候,浮動元素會向下移動,直到他放得下為止(❷)
以前面的例子來說(見下圖示),前三張卡占滿了頁面,沒空間讓第四張(#4)放,所以就沿著#3的邊緣向下移動,直到他可以往左擠進頁面裡,然後當左邊碰到#2時便停下來了。當發生這種狀況時,就跑版了。
把 grid system 的 css 設定放進 codepen 後跑出來的結果就會像下面這樣:
clearfix 的功能: 清除浮動(float)
DevTools 點開 clearfix
查看 css 可以看到設定如下:
.clearfix::before, .clearfix::after{
content: " ";
display: table;
}.clearfix::after{
clear: both;
}
也就是以 ① :before
, ::after
偽元素搭配 ② display: table
以及 ③ clear: both
達成
把 clearfix
的css設定放搭配 grid system 後跑出來的結果如下,先看過之後再來解釋各項功用。( content
跟 clearfix
包夾的內容有修改過,方便觀察)
① ::before 與 ::after 偽元素
::before
/::after
的作用簡單說就是在原本的元素之前/之後加入內容(❸)。下面是加入 ::before
/::after
但沒有設定 display
跟 clear
的狀況。如果試著用滑鼠框選,會發現 div.bf
包夾的字可以被選取,但以 :before
/:after
顯示的卻無法,也就是 ::before
/::after
的內容是像幽靈一樣看得到摸不到的東西。所以 clearfix
將 ::before
/::after
設定內容為空格,可以幫助帶入所需的屬性而不影響內容。
div.bf
在設定 ::before
/::after
屬性前,如果不包夾東西,它的高度為0,貼在 container
頂端,且寬度等同 container
。加入 ::before
/::after
屬性後,div.bf
因為有包含了 ::before
/::after
兩個 inline 元素,高度被撐開(如下圖)。所以前面以為是把三張卡片包起來,事實上只是因為 clearfix
高度較高,看起來不含任何元素,且跟浮動的卡片屬於不同層,導致看起來像是程式碼寫在下面卻能夠把上面的東西包起來。
②display: table
接下來在 ::before
/::after
內加入 display: table
,產生結果入下面所示。::before
/::after
因為設定成 display: table
,進而產生匿名框並觸發BFC(❹,❺),所以變成垂直排列。
③ clear: both
經由設定 clear: left
/ clear: right
/ clear: both
,可以用來確保當前元素左側/右側/兩側不會有浮動元素(❻)。不過 clear
有兩個特性,一是只對塊級元素( block-level elements )發生作用(❷之9.5.2),二是只對元素本身布局起作用(❻),這兩點會牽扯到為什麼要設定 display
。
把前面①的程式碼在 :after
中加入 clear: both
,會發現並沒有奏效,因為 before, hey, after 預設 display
為 inline
,並不屬於塊級元素,所以 clear
不會作用。
再回第一個 codepen 中看,經由 display: table
設定,div.clearfix
變成了三個塊級元素。after 受到 clear: both
的影響,需要保持左右兩側沒有浮動元素的狀態,所以將自身往下移動,直到靠到 container
左側,這時左邊就沒有浮動元素了。同理,因為右側不能有浮動元素,造成屬性為靠左浮動的 box4 需要換行並向左靠,所以 box4 就停在了 after 的下方。
其實 row 裡面就有 clearfix
Bootstrap 對於 grid system 設定中有個使用條件: column
必須要放在 row
裡面,那是因為 row
帶有 clearfix
的功能(❼),從 DevTools 也能看到同樣設定。也因為這樣,當你產生新的 row
時,裡面的第一個 col
一定會在最左邊(如果沒 offset
),且一定會在上一個 row
的下面。
所以最前面提到插入 div.clearfix
的解法,還有另外一種方法就是改寫成每列都用 row
包起來。當需要使用 RWD 時,每列顯示 column
數量會不同,這時就需要判斷是否要清除浮動。以 div.clearfix
的方法來說只要判斷是否要再加入 hidden-sm
、hidden-md
還是 hidden-lg
就好,一行即可解決;但是改寫成用 row
包夾時卻要雙倍工,程式碼會變得更雜,所以最佳解仍是 div.clearfix
。
補充
如果試著把 ::before
內的設定刪掉,或是把 display: table
換成 display: block
的話其實還是能夠清除浮動,因為:
::before
內的設定其實是為了防止 margin collapsedisplay: table
是為了顧及在 IE 6/7 上顯示的問題(❽)。
2018.07 更新
改版成 Bootstrap 4 後,有些語法與 Bootstrap 3 有差異:
- Responsive utilities: hidden-xs 之類的
<!-- Bootstrap 3 使用 -->
<div class="clearfix hidden-xs"></div><!-- Bootstrap 4 使用 -->
<div class="clearfix d-none d-sm-block"></div>
詳細差異請見參考資料9
2. clearfix 的內容變更:
/* Bootstrap 3 使用 */
.clearfix::before, .clearfix::after{
content: " ";
display: table;
}.clearfix::after{
clear: both;
}/* Bootstrap 4 使用 */
.clearfix::after{
content: " ";
display: block;
clear: both;
}
Bootstrap 4 拿掉了 ::before
並將 display
改為 block
(見參考資料10)。由於 display: table
可能會產生一些問題(可見參考資料11),而他的出現又是為了 IE 6/7 支援性的關係,微軟都要把 IE 6/7淘汰掉了,換 display: block
也是正常的。