可能是最全面最易懂的解析前端浮動的文章

原始碼終結者發表於2018-10-10

寫在最前

習慣性去谷歌翻譯看了看float的解釋:

image
其中有一句這樣寫的:

she relaxed, floating gently in the water

瞬間浮想聯翩,一個女神,輕輕地漂浮在水中。開心的拍打著水花,哇靠。。。

不想了,人間不值得,步入正題吧,上面美妙的畫面中,我們可以看到,女神還是擠佔了水的空間,女神是浮動的。那麼來,好了,編不下去了,直接開題吧。。。

我覺得很多人連float是啥意識都不知道,要知道很多特性的原理是和其命名的單詞或者字母有密切關聯的,不是隨便命名的。從名字中可以看到一些當初設計的初衷。

找出問題是關鍵

問自己三個問題:

第一 浮動會造成什麼影響?

第二,如何解決這些因為浮動而造成的影響?

第三,bfc原理?
複製程式碼

其實我個人理解,浮動造成的最核心的問題就是破壞了文件流,那麼問題來了,float破壞了文件流,為什麼還要設計這個api,我查了一些資料最後才知道,這是因為當初設計float的目的是為了能實現文字能夠環繞圖片的排版功能,也就是我們有時會納悶的一點:設定浮動後,還是會擠佔容器中的文字內容。

比如看下面這段程式碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>float實現浮動</title>
</head>
<style>
    .z1{
        height: 200px;
        width: 200px;
        float: left;
        text-align: center;
        line-height: 200px;
        background: skyblue;
    }
    .fu {
        width: 400px;
    }
</style>
<body>
<div class="fu clearfix">
    <div class="z1">設定了float為left的圖片</div>
    <div class="z2">你看,我沒有被浮動哥哥擋住哦,這是一段神奇旅行,一天我遇上了白雪公主</div>
</div>
</body>
</html>
複製程式碼

效果圖如下:

image

看到這,是不是有些理解了。從上圖會發現,即使圖片浮動了,脫離了正常文件流,也覆蓋在沒有浮動的元素上了,但是其並沒有將文字內容也覆蓋掉,這也證實了float這個api當初被設計出來的主要目的:實現文字環繞圖片排版功能。

當想到這時,我突然意識到,其他佈局模式是什麼樣子,然後進行了實驗。去掉容器z1float屬性,增加了position屬性,程式碼如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>absolute實現浮動</title>
</head>
<style>
    .z1{
        height: 200px;
        width: 200px;
        box-sizing: border-box;
        position: absolute;
        text-align: center;
        background: skyblue;
        padding-top: 80px;
        opacity: 0.8;
    }
    .fu {
        width: 400px;
    }
</style>
<body>
<div class="fu clearfix">
    <div class="z1">設定了positon為absolute的圖片</div>
    <div class="z2">你看,我被absolute哥哥擋住哦,這是一段神奇旅行,一天我遇上了白雪公主</div>
</div>
</body>
</html>
複製程式碼

效果圖如下:

image

我們可以看到,設定absolute的容器,才是意義上的完全脫離正常文件流。覆蓋在當前位置上的所有容器和文字內容之上。對比思考一下,會發現這又證明了float被設計出來的主要目的。如果能理解成這樣,我覺得對於不同業務上該用什麼方式清除float,或者說該用什麼來代替float,將會有個很明確的方向。

其實你會發現,absolutefloat都不會去覆蓋掉在他們之前的正常文件流,這應該和瀏覽器渲染機制有關係,會從上到下依次渲染內容,渲染成功後,就不會因為後續元素浮動而使其被覆蓋住(不考慮使用fix等強行覆蓋的情況)。

簡易程式碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>清除float浮動</title>
</head>
<style>
    .z1{
        height: 200px;
        width: 200px;
        box-sizing: border-box;
        float: left;
        text-align: center;
        background: skyblue;
        padding-top: 80px;
        opacity: 0.5;
    }
    .z2 {
        background: yellow
    }
    .z3 {
        background: red;
    }
    .z4 {
        background: green;
    }
    .z5 {
        background: pink;
    }
</style>
<body>
<div class="fu">
    <div class="z2">沒有設定任何浮動的容器,背景為黃色</div>
    <div class="z3">沒有設定任何浮動的容器,背景為紅色</div>
    <div class="z1">設定了浮動的元素,opacity為0.5</div>
    <div class="z4">沒有設定任何浮動的容器,背景為綠色</div>
    <div class="z5">沒有設定任何浮動的容器,背景為粉色</div>
</div>
</body>
</html>
複製程式碼

效果圖如下:

image

從圖中的標註和說明我們可以清晰的知道,float不會影響前面已經渲染好的文件,而會影響在其後面將要渲染的文件。那麼問題來了,怎樣才能消除因為z1的浮動而對z4,z5造成的影響呢?

首先我們要知道,z1這個浮動造成了哪些影響,影響如下:

第一個影響:影響了z4,z5的佈局。

第二個影響:影響了父容器的高度,正常父元素的高度是自適應的,高度為其包含的內容總高度,而內部元素的浮動造成了父容器高度塌陷。

第三個影響:父容器高度塌陷了,將會影響和父元素同級的文件佈局。

下面的程式碼可以完美的詮釋這些影響:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>清除float浮動</title>
</head>
<style>
    .z1{
        height: 200px;
        width: 200px;
        box-sizing: border-box;
        float: left;
        text-align: center;
        background: skyblue;
        padding-top: 80px;
        opacity: 0.5;
    }
    .z2 {
        background: yellow
    }
    .z3 {
        background: red;
    }
    .z4 {
        background: green;

    }
    .z5 {
        background: pink;
    }
    .z6 {
        color: #fff;
        background: black;
    }
    .z7 {
        color: #fff;
        background: blue;
    }
    .fu {
        /* overflow: hidden; */
    }
</style>
<body>
<div class="fu">
    <div class="z2">沒有設定任何浮動的容器, 背景為黃色</div>
    <div class="z3">沒有設定任何浮動的容器, 背景為紅色</div>
    <div class="z1">設定了浮動的元素, opacity為0.5</div>
    <div class="z4">沒有設定任何浮動的容器, 背景為綠色</div>
    <div class="z5">沒有設定任何浮動的容器, 背景為粉色</div>
</div>
<div class="z6">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
<div class="z7">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
</body>
</html>
複製程式碼

效果圖如下:

image
通過圖中的標註我們可以很清晰看到上面提到的三個影響,那麼影響也清晰的看到了,下面該如何去解決這些影響呢?

解決思路很重要

要解決這三個影響,需要從兩個方向思考:

第一個方向:解決父元素給其同級的元素造成的影響,我比喻成解決外部矛盾

第二個方向:解決父級元素內部的浮動元素對其同級元素的影響,我比喻成解決內部矛盾

俗話說的好,家醜不可外揚,那麼來,現在就先解決外部矛盾,怎麼解決呢,解決的思想,無非就是讓父級元素的高度不再塌陷,把浮動元素的高度算進去。記住一個關鍵點,這時候,內部矛盾還是存在的。比如浮動元素和其後續的同級元素有高度重疊。

解決外部矛盾

觸發bfc

第一個是觸發bfc,為什麼呢,因為觸發bfc後,高度會包括浮動元素的高度。怎麼觸發呢,可以給父級元素設定overflow:auto;對於其他的觸發bfc方式,我就不說了,我主要說一下原理。程式碼如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>清除float浮動</title>
</head>
<style>
    .z1{
        height: 200px;
        width: 200px;
        box-sizing: border-box;
        float: left;
        text-align: center;
        background: skyblue;
        padding-top: 80px;
        opacity: 0.5;
    }
    .z2 {
        background: yellow
    }
    .z3 {
        background: red;
    }
    .z4 {
        background: green;

    }
    .z5 {
        background: pink;
    }
    .z6 {
        color: #fff;
        background: black;
    }
    .z7 {
        color: #fff;
        background: blue;
    }
    .fu {
        overflow: hidden;
    }
</style>
<body>
<div class="fu">
    <div class="z2">沒有設定任何浮動的容器, 背景為黃色</div>
    <div class="z3">沒有設定任何浮動的容器, 背景為紅色</div>
    <div class="z1">設定了浮動的元素, opacity為0.5</div>
    <div class="z4">沒有設定任何浮動的容器, 背景為綠色</div>
    <div class="z5">沒有設定任何浮動的容器, 背景為粉色</div>
</div>
<div class="z6">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
<div class="z7">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
</body>
</html>
複製程式碼

效果圖如下:

image
圖中可以很清晰的看出,觸發父元素的bfc後,外部矛盾解決了,但是內部的矛盾還沒有解決。那麼現在就開始解決內部矛盾。怎麼解決內部矛盾呢,也就是父元素內部的浮動元素的高度和後面的同級元素的高度有重疊呢。這個時候,我們先不著急解決內部矛盾,我們來看一下,另一種解決外部矛盾的方式。

clear原理

給父元素增加偽元素:程式碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>清除float浮動</title>
</head>
<style>
    .z1{
        height: 200px;
        width: 200px;
        box-sizing: border-box;
        float: left;
        text-align: center;
        background: skyblue;
        padding-top: 80px;
        opacity: 0.5;
    }
    .z2 {
        background: yellow
    }
    .z3 {
        background: red;
    }
    .z4 {
        background: green;
        /* clear: left */
    }
    .z5 {
        background: pink;
    }
    .z6 {
        color: #fff;
        background: black;
    }
    .z7 {
        color: #fff;
        background: blue;
    }
    .fu {
    }
    .clearfix:after {
        display: block;
        overflow: hidden;
        content: '偽元素的內容哦';
        clear: both;
        height: 0;
        background: slateblue;
    }
</style>
<body>
<div class="fu clearfix">
    <div class="z2">沒有設定任何浮動的容器, 背景為黃色</div>
    <div class="z3">沒有設定任何浮動的容器, 背景為紅色</div>
    <div class="z1">設定了浮動的元素, opacity為0.5</div>
    <div class="z4">沒有設定任何浮動的容器, 背景為綠色</div>
    <div class="z5">沒有設定任何浮動的容器, 背景為粉色</div>
</div>
<div class="z6">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
<div class="z7">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
</body>
</html>
複製程式碼

很多人不清楚用偽元素清除浮動的原理是什麼,為什麼給父元素加這個偽元素,可以清除父元素的浮動。這裡我故意在偽元素的content寫了一些文字內容,同時加了背景色,有點像基佬色。。。


OK,先看整體效果圖吧:

image
不出意外,從上圖可以看到,外部矛盾被解決了。這只是開始,大家眼睛盯好,繼續看下面截圖:


image
從圖中標註可以看出,為什麼偽元素要設定display:block,繼續看下一個截圖。


image
從上圖中可以知道,為什麼height要設定成0了。如果content不是空字串,那麼就會在頁面中顯示內容。但其實清除浮動時,content都會寫成空的字串,如果content裡面只設定成''空的字元,那麼height也可以不寫,包括overflow也可以不寫,寫heightoverflow都是為了程式碼的魯棒性。不過有個很重要,content這個屬性,必須要寫,不寫content,是沒法清除浮動的。


最重要的知識點要來了,請看兩個截圖:

image
我故意讓content顯示出來,會發現偽元素清除浮動的核心原理其實是在給父元素增加塊級容器,同時對塊級容器設定clear屬性,使其能夠清除自身的浮動,從而正常按照塊級容器排列方式那樣排列在浮動元素的下面。同時,父元素的同級元素也會正常排列在偽元素形成的塊級元素後面,而不受浮動影響。


下面是幹掉clear屬性後的截圖:

image
發現清除浮動失敗了,其實可以看出,給父元素增加一個偽元素來清除浮動的本質,是通過

給父元素再加一個塊級子容器,當然這個也就是父元素的最後一個塊級子容器了。同時給這個塊級子容器設定clear屬性來清除其浮動,這樣這個子容器就能排列在浮動元素的後面,同時也把父元素的高度撐起來了。那麼父元素的同級元素也能正常排列了。所以這個子容器不能有高度和內容,不然會影響父元素的佈局。

寫到這,外部矛盾的解決方式和各自的原理已經說的很清楚了。那麼內部矛盾怎麼解決呢?

其實,解決內部矛盾的原理和解決外部矛盾的第二種方式的原理是一樣的,通過給被浮動影響的第一個元素進行清除浮動,就可以使後面的元素也不會受到浮動影響了。程式碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>清除float浮動</title>
</head>
<style>
    .z1{
        height: 200px;
        width: 200px;
        box-sizing: border-box;
        float: left;
        text-align: center;
        background: skyblue;
        padding-top: 80px;
        opacity: 0.5;
    }
    .z2 {
        background: yellow
    }
    .z3 {
        background: red;
    }
    .z4 {
        background: green;
        clear:both;
    }
    .z5 {
        background: pink;
    }
    .z6 {
        color: #fff;
        background: black;
    }
    .z7 {
        color: #fff;
        background: blue;
    }
</style>
<body>
<div class="fu">
    <div class="z2">沒有設定任何浮動的容器, 背景為黃色</div>
    <div class="z3">沒有設定任何浮動的容器, 背景為紅色</div>
    <div class="z1">設定了浮動的元素, opacity為0.5</div>
    <div class="z4">沒有設定任何浮動的容器, 背景為綠色</div>
    <div class="z5">沒有設定任何浮動的容器, 背景為粉色</div>
</div>
<div class="z6">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
<div class="z7">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
</body>
</html>
複製程式碼

效果圖如下:

image

給內部元素設定clear:both;清除浮動後,會直接解決內部矛盾和外部矛盾。可能會有人想,如果z4容器後面又有一個浮動元素呢,這裡我不想再解釋了,因為可遞迴得出原理都是一樣的,但是吧,我還是上個程式碼分析一下吧:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>清除float浮動</title>
</head>
<style>
    .z1{
        height: 200px;
        width: 200px;
        box-sizing: border-box;
        float: left;
        text-align: center;
        background: skyblue;
        padding-top: 80px;
        opacity: 0.5;
    }
    .z2 {
        background: yellow
    }
    .z3 {
        background: red;
    }
    .z4 {
        background: green;
        clear:both;
    }
    .z5 {
        background: pink;
        /* clear:both */
    }
    .z6 {
        color: #fff;
        background: black;
    }
    .z7 {
        color: #fff;
        background: blue;
    }
    .fu {
        overflow: auto;
    }
</style>
<body>
<div class="fu">
    <div class="z2">沒有設定任何浮動的容器, 背景為黃色</div>
    <div class="z3">沒有設定任何浮動的容器, 背景為紅色</div>
    <div class="z1">設定了浮動的元素, opacity為0.5</div>
    <div class="z4">沒有設定任何浮動的容器, 背景為綠色</div>
    <div class="z1">設定了浮動的元素, opacity為0.5</div>
    <div class="z5">沒有設定任何浮動的容器, 背景為粉色</div>
</div>
<div class="z6">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
<div class="z7">和父級元素同級的容器, 沒有設定任何浮動, 背景為綠色</div>
</body>
</html>
複製程式碼

效果圖如下幾張截圖:

父元素沒有清除浮動,外部矛盾,內部矛盾都存在

image

父元素使用bfc清除浮動,外部矛盾解決,內部矛盾還存在

image

通過給父元素中的浮動元素後面的第一個同級塊級元素設定clear清除浮動,內部矛盾解決,外部矛盾也解決。

image

對於clear還有leftright,這個就不說了,api的事情,正常both就可以了。寫到此,差不多要結束了。最後再總結一下吧:

不同業務中可能需要不同的清除浮動的方式,不論選擇哪一種方式,都避不開外部矛盾和內部矛盾,你的業務需要保留內部矛盾,只解決外部矛盾,還是外部矛盾和內部矛盾都解決。這些得需要根據業務的特點來決定。其次,是使用bfc還是clear還是偽元素,使用bfc的話使用哪種方式去觸發。這也是根據業務的特點來決定。

文末的可愛宣告:如果轉發或者引用,請貼上原連結,尊重一下熬夜完成的勞動成果?。文章可能有一些錯誤,歡迎評論指出,也歡迎一起討論。文章可能寫的不夠好,還請多多包涵。為了前端,我也是操碎了心,人生苦短,我學前端,多一點貢獻,多一分開心~

相關文章