css 快速入門 系列 —— 浮動

浮動

以 mdn float 文檔 為基礎,逐一介紹浮動的本質、浮動的諸多特性、清除浮動以及塊格式化上下文(bfc)。

概念

當一個元素浮動之後,它會被移出正常的文檔流,然後向左或者向右平移,一直平移直到碰到了所處的容器的邊框,或者碰到另外一個浮動的元素。

卡住

如果包含塊太窄,無法容納容納水平排列的多個浮動元素,那麼其他浮動元素會向下移。如果浮動元素高度不同,就會出現」卡住「的情況:

box 1
box 2
box 3

浮動與行框盒子

如果文字很長,需要兩行顯示,而且在長文字後面跟著一個向右浮動的元素,請問該浮動元素會顯示在第一行還是第二行?

請看示例:

Lorem ipsum dolor sit amet consectetur more

浮動元素會顯示在第二行。因為浮動元素會根據行框盒子進行定位,這裡有兩行,也就是兩個行框盒子,而 」more「 在第二行的行框盒子後面。

float 的本質

創建 float 或許是為了實現文字環繞圖片的效果。就像這樣:

Lorem ipsum, dolor sit amet consectetur adipisicing elit. Porro vero aliquid nisi nobis omnis iste,

其中圖片是左浮動 float:left;

float 從文檔流中脫離

浮動元素雖然從文檔流中脫離,但有時還是會影響不浮動的元素。

position:absolute 也會從文檔流中脫離,但不會影響不浮動的元素。我們將 文字環繞圖片 的例子用絕對定位重寫一下:

Lorem ipsum, dolor sit amet consectetur adipisicing elit. Porro vero aliquid nisi nobis omnis iste,

塊布局

由於 float 意味著使用塊布局,所以該浮動元素的 display 的計算值會發生變化。通常會變成 block。請看示例:

<span id="id1" style="float:left"></span>

let elem = document.getElementById('id1')
let display = window.getComputedStyle(elem).getPropertyValue('display')
// display:  block
console.log('display: ', display);

所以不要再寫如下程式碼:

span {
 float: left; 
 display: block; /* 多餘 */ 
}

Tip: float 元素的 display 變化值,對應關係表請看 mdn。

浮動使高度塌陷

如果一個元素里只有浮動元素,那它的高度會是 0。就像這樣:

// 核心程式碼:
<div style="border:1px solid red;">
    <img style="float:left">
</div>

浮動使父元素高度塌陷,是因為浮動元素不在文檔流中。

clear

可以使用 clear 屬性來處理浮動使父元素高度塌陷的問題。

clear: none | left | right | both

字面翻譯為」清除左浮動「,或」清除右浮動「,其實這樣理解有誤。請看示例:

Lorem ipsum, dolor
// 核心程式碼:
<div style="border:1px solid red;">
    <img style="float:left">
    Lorem ipsum, dolor
    <div style="border:1px solid blue;clear:both;"></div>
</div

雖然我們使用了 clear:both,卻只解決了父元素高度塌陷問題,而浮動還是存在,因為文字仍舊環繞著圖片,故浮動沒有消除。

實際上,clear 表示框(或行框)的哪些邊(左邊或右邊)不應該挨著浮動框。對於 clear 元素,瀏覽器在該元素頂部添加足夠多的外邊距,使元素垂直下降到浮動框下面。

更通俗的說法是」左側抗浮動「、」右側抗浮動「、」兩側抗浮動「。

clear:both

clear:both 不是兩側抗浮動,準確的理解應該是左側抗浮動,或右側抗浮動。

思考這麼一個情況:如果有三個向左浮動的元素,同時在第二個元素上添加 clear:both,請問是變成兩行還是三行?
答:是 2 行。

請看示例:

為什麼會這樣?因為在 mdn float 文檔 中寫著」清除之前」的浮動,也就是說不管後端的浮動

Tip:既然只管前面的浮動,而元素要麼左浮動,要麼右浮動,所以 clear:left、clear:right 就沒什麼用了,直接寫 clear:both 就好。

clearfix

如果要讓父元素包含浮動的子元素,有一種方法叫做 clearfix,即 clear 一個不浮動的 ::after 偽元素。請看效果:

// 核心程式碼
.f-clearfix::after{display:block;content:'';clear:both;}

<div class="f-clearfix">
    <img style="float:left">
</div>

Tip: clear 屬性只有塊級元素才有效。故 display 也可以設置為 table、list-item。

一側定寬的兩欄自適應布局

效果如下:

Lorem ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis, nobis.
// 核心程式碼
.f-clearfix::after{display:block;content:'';clear:both;}

<!-- f-clearfix 解決父元素高度塌陷,margin-left 解決文字環繞圖片 -->
<div class="f-clearfix">
    <img style="float:left;width:60px">
    <div style="margin-left:70px;">Lorem ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis, nobis.</div>
</div>

注:浮動圖片與右側盒子的間距,這裡使用圖片的寬度+間隙寬度(margin-left:60px + 10px),如果只在圖片上使用 margin-right:10px,仍然會存在文字環繞圖片的問題。

margin 不會合併

浮動元素的垂直 margin 不會合併。請看示例:

// 核心程式碼
<div class='float:left;margin:10px'></div>
<div class='float:left;margin:10px'></div>

由於沒有發生合併,所以兩個盒子的垂直外邊距是 10px + 10px。

塊格式化上下文(BFC)

如果一個元素具有 bfc,那麼它的內部元素在怎麼折騰,也不會影響外部元素,而且也不會被外部元素所影響。

浮動使高度塌陷,我們有時會在父元素上設置 overflow:hidden 來解決此問題。就像這樣:

Lorem, ipsum dolor.

// 核心程式碼

<div class="overflow:hidden">
    <img style="float:left">
</div>
<p>Lorem, ipsum dolor.</p>

創建 bfc 有很多方式,overflow:hidden 就是其中一種。如果不將浮動帶來的高度塌陷這個問題解決,那麼就會影響到後面元素的布局,這顯然會違背 bfc 的特性。

overflow:hidden/auto 是創建 bfc 眾多方式中為數不多的好方法。如果不考慮兼容性,display:flow-root 則是創建 fbc 最佳方式

Tip: 更多創建 bfc 的方式請看 mdn bfc。

bfc 與自適應布局

bfc 除了可以清除 float 的影響、去除 margin 重疊,還可以實現更健壯的自適應布局。

我們使用 bfc 重寫上面的「一側定寬的兩欄自適應布局」:

Lorem ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis, nobis.
// 核心程式碼

<!-- `overflow:hidden;` 解決父元素高度塌陷 -->
<div class="overflow:hidden;">
    <!-- `margin-right:10px` 增加與右側盒子的間距 -->
    <img style="float:left;width:60px;margin-right:10px">
    <!-- `overflow:hidden;` 解決文字環繞圖片 -->
    <div style="overflow:hidden;">Lorem ipsum dolor sit amet consectetur adipisicing elit. Perspiciatis, nobis.</div>
</div>

Tip: 如果你的產品只需要支援 firefox 53+、chrome 58+、edge 79+,你就可以使用 display:flow-root 來創建無副作用的 bfc。並且會變成塊級元素。

Tags: