CSS深入理解流體特性和BFC特性下多欄自適應佈局

一、開篇之言

要說web上實現兩欄自適應佈局的方法,一雙手都數不過來。不知大家有沒有細想過,爲什麼這些方法可以實現自適應佈局呢?

本文就將深入探討下流體特性和BFC特性下的兩欄自適應佈局,還是針對傳統佈局。一些現代佈局,如彈性盒子模型佈局(Flexbox Layout),格柵佈局(Grid Layout)不在本文探討之類。

有些人看了個標題,以及看了前面一兩段,發現,都是我知道的概念嘛,什麼流動性,什麼BFC~~於是,就悻悻離開了。這就是我們常說的浮躁,保持一顆謙遜的心,細細閱讀,你會發現,其中一定有你所沒有關注過的地方,所謂三人行必有我師。沒錯,這句話就是寫給你看的,同時也是自我內省與監督。

二、塊狀元素的流體特性與自適應佈局

流體特性
塊狀水平元素,如div元素(下同),在默認情況下(非浮動、絕對定位等),水平方向會自動填滿外部的容器;如果有margin-left/margin-rightpadding-left/padding-rightborder-left-width/border-right-width等,實際內容區域會響應變窄。

一圖勝千言,一例勝千圖。可參考下面例子,感受下div元素的流體特性:

圖片寬度一直width:100%,依次點擊3個按鈕,結果隨着marginpaddingborder的出現,其可用寬度自動跟着減小,形成了自適應效果。就像放在容器中的水流一樣,內容區域會隨着marginpaddingborder的出現自動填滿剩餘空間,這就是塊狀元素的流體特性。

流體特性
下面,我們稍微做一個調整,div距離容器左側margin 150像素,裏面的圖片同樣100%自適應內容區域。HTML如下:

.flow-box {
    width: 500px; background-color: #eee; overflow:auto; resize:horizontal;
}
.flow-content {
    margin-left: 150px;
}
<div class="flow-box">
    <div class="flow-content"><img src="mm1.jpg" width="100%" height="190"></div>
</div>

圖片右下角有兩道斜槓,我們可以resize拉伸(現代瀏覽器,且非移動訪問),會發現,左側永遠150像素留白,而圖片隨着容器寬度變化而自適應變化了。

此時,我們需要好好利用左側150像素的留白間距,豈不是就可以實現兩欄自適應效果!?

爲了不影響原本的流體特性,我們可以使用破壞性屬性,如浮動(float:left),或者絕對定位(position:absolute)。

我們直接HTML如下調整即可:

<div class="flow-box">
    <img src="mm1.jpg" width="128" style="float:left;">
    <div class="flow-content"><img src="mm1.jpg" width="100%" height="190"></div>
</div>
<div class="flow-box">
    <img src="mm1.jpg" width="128" style="position:absolute;">
    <div class="flow-content"><img src="mm1.jpg" width="100%" height="190"></div>
</div>

結果分別如下:

當然,你可以左側有多個浮動,或者左浮動+右浮動。於是,我們不僅可以實現兩欄自適應效果,多欄自適應效果也不在話下。

然而,利用塊狀元素流體特性實現的自適應佈局有個不足,就是,我們需要知道浮動或絕對定位內容的尺寸。然後,流體內容纔能有對應的marginpaddingborder值進行位置修正。於是,問題來了,我們沒法單純使用一個公用的類名,類似.clearfix這樣,整站通用。因爲不同自適應場景的留白距離是不一樣的。

此時,我們可以利用塊狀元素的BFC特定實現更強大更智能的多欄自適應佈局(本文重點)。

三、元素的BFC特性與自適應佈局

1. BFC元素簡介與基本表現
BFC全稱”Block Formatting Context”, 中文爲“塊級格式化上下文”。啪啦啪啦特性什麼的,一言難盡,大家可以自行去查找,我這裏不詳述,免得亂了主次,總之,記住這麼一句話:BFC元素特性表現原則就是,內部子元素再怎麼翻江倒海,翻雲覆雨都不會影響外部的元素。所以,避免margin穿透啊,清除浮動什麼的也好理解了。

抱詐(和諧)與釣魚

什麼時候會觸發BFC呢?常見的如下:

  • float的值不爲none
  • overflow的值爲auto,scrollhidden
  • display的值爲table-celltable-captioninline-block中的任何一個。
  • position的值不爲relativestatic

BFC特性很多,而我們這裏,只關心一個,和float元素做相鄰兄弟時候的表現。

如果是上面介紹的流體特性div, 當其和浮動元素當兄弟的時候,是覆蓋的關係(可以腦補下文字環繞圖片效果)。但是,元素BFC化後,本着“裏面驚天抱詐(和諧)炸成鬼,外面依然泰然釣大魚”的原則,自然是不會與浮動重疊的(你想啊,要是出來個clear:both還不跟外面浮動幹上一架啊),因此,塊狀相鄰,點擊下面按鈕感受下。

會發現,普通流體元素BFC後,爲了和浮動元素不產生任何交集,順着浮動邊緣形成自己的封閉上下文。如下截圖:
BFC與浮動邊緣對齊~

同時,元素原本的流體特性依然保留了。哈,這個很重要,也就是,雖然不與浮動交集,自動退避浮動元素寬度的距離,但,本身作爲普通元素的流動性依然存在,反映在佈局上就是自動填滿除去浮動內容以外的剩餘空間。喲,這不就是自適應佈局嘛!!

2. BFC自適應佈局模塊間的間距
然而,模塊過於親密接觸,可能不是我們想要的。一般而言,我們需要一點間距。

說到間距,我們的第一反應就是margin. 於是,我們給BFC元素增加一個margin-left:20px, CSS代碼如下:

.float-left {
    float: left;
}
.follow-content {
    margin-left: 20px;
    background-color: #cad5eb;
    overflow: hidden;
}

結果……納尼~  怎麼還是像狗屁膏藥貼在一起啊??
margin-left:20px無效截圖

您可以狠狠地點擊這裏:BFC元素增加一個margin無效demo

實際上,這裏的margin並不是無效,而是值不夠大,鞭長莫及啊!

朕真是鞭長莫及

用一個形象的Gif表示就是下面這樣:
女生拳打腳踢夠不到男孩

左側浮動的圖片就好比上面Gif圖片中男孩的胳膊,妹子就是BFC元素,結果兩人緊密接觸。然後,margin-left就是妹子的胳膊個腳,雖然也甩出去了,可惜長度有限,於是,毫無影響。

如果按照上面的解釋,那我們把margin-left:20px改成margin-left:150px就應該有間距了? 一試便知!

.float-left {
    float: left;
}
.follow-content {
    margin-left: 150px;
    background-color: #cad5eb;
    overflow: hidden;
}

結果,噹噹噹當:
margin值很大收穫的間距

注意:我這裏舉margin這個例子,不是讓大家這樣使用,只是爲了讓大家可以深入理解BFC元素與浮動元素混排的特性表現。實際開發,我們完全沒有必要對BFC元素設置margin, 因爲又回到了流體佈局,明明是固定的15像素間距,但是,每個佈局都要寫一個不同的margin值,完全沒有重用價值。

但是,間距部分的高潮來了!

我們可以使用浮動元素的margin-right或者padding-right輕鬆實現間距效果。間距是20像素,直接:

.float-left {
    float: left;
    margin-right: 20px;
}

與浮動元素的寬度是多少沒有任何關係。不僅如此,我們還可以使用BFC元素的padding-left撐開間距(雖然margin-left作用雞肋)。

於是,我們可能就會有:

.l { float: left; }
.ovh { overflow: hidden; }

的自適應固定搭配。再配合zxx.lib.cssCSS樣式庫的marginpadding家族,快速佈局可謂所向披靡。

3. 與純流體特性佈局的優勢
BFC自適應佈局優勢我總結了下面2點:

  1. 自適應內容由於封閉,更健壯,容錯性強。比方說,內部clear:both不會與兄弟float產生矛盾。而純流體佈局,clear:both會讓後面內容無法和float元素在一個水平上,產生布局問題。
  2. 自適應內容自動填滿浮動以爲區域,無需關心浮動元素寬度,可以整站大規模應用。而純流體佈局,需要大小不確定的margin/padding等值撐開合適間距,無法CSS組件化。

如下效果,圖片能大能小,佈局依然良好:

而CSS代碼就是非常簡單的:

.float-left {
    float: left; margin-right: 20px; 
}
.bfc-content {
    overflow: hidden; background-color: #beceeb;
}

可以說,有了BFC自適應佈局,基本上沒有了純流體特性佈局存在的價值。然而,只是理論上如此。如果,BFC自適應佈局真那麼吊炸天,那爲何並沒有口口相傳呢?

那我們就得進一步深入理解了。

4. BFC元素家族與自適應佈局面面觀
理論上,任何BFC元素和浮動搞基的時候,都可以實現自動填充的自適應佈局。

但是,由於絕大多數的觸發BFC的屬性自身有一些古怪的特性,所以,實際操作的時候,能兼顧流體特性和BFC特性來實現無敵自適應佈局的屬性並不多。下面我們牽驢遛馬一個一個瞅瞅(類似行爲僅出1個代表示意,你懂的,如float:left/right):

  1. float:left 浮動元素本身BFC化,然而浮動元素有破壞性和包裹性,失去了元素本身的流體自適應性,因此,無法用來實現自動填滿容器的自適應佈局。不過,其因兼容性還算良好,與堆積木這種現實認知匹配,上手簡單,因此在舊時代被大肆使用,也就是常說的“浮動佈局”,也算陰差陽錯開創了自己的一套佈局。
  2. position:absolute 這個脫離文檔流有些嚴重,過於清高,不跟普通小夥伴玩耍,我就不說什麼了……
  3. overflow:hidden 這個超棒的哦!不像浮動和絕對定位,玩得有點過。也就是溢出剪裁什麼的,本身還是個很普通的元素。因此,塊狀元素的流體特性保存相當完好,附上BFC的獨立區域特性,可謂如虎添翼,宇宙無敵!哈無誒瓦(However), 就跟清除浮動:
    .clearfix { overflow: hidden; _zoom: 1; }

    一樣。由於很多場景我們是不能overflow:hidden的,因此,無法作爲一個通用CSS類整站大規模使用。因此,float+overflow的自適應佈局,我們可以在局部(你確定不會有什麼被剪裁的情況下)很happy地使用。

  4. display:inline-block CSS屆最偉大的聲明之一,但是,在這裏,就有些捉襟見肘了。display:inline-block會讓元素尺寸包裹收縮,完全就不是我們想要的block水平的流動特性。唉,只能是一聲嘆氣一槍斃掉的命!然而,峯迴路轉,世事難料。大家應該知道,IE6/IE7瀏覽器下,block水平的元素設置display:inline-block元素還是block水平,也就是還是會自適應容器的可用寬度顯示。於是,我們就陰差陽錯得到一個比overflow:hidden更牛逼的聲明,即BFC特性加身,又流體特性保留。
    .float-left {
        float: left;
    }
    .bfc-content {
        display: inline-block;
    }

    當然,*zoom: 1也是類似效果,不過只適用於低級的IE瀏覽器,如IE7~

  5. display:table-cell 讓元素表現得像單元格一樣,IE8+以上瀏覽器才支持。跟display:inline-block一樣,會跟隨內部元素的寬度顯示,看樣子也是不合適的命。但是,單元格有個非常神奇的特性,就是你寬度值設置地再大,大到西伯利亞,實際寬度也不會超過表格容器的寬度。
    表格單元格不會超出表格寬度特性

    因此,如果我們把display:table-cell這個BFC元素寬度設置很大,比方說3000像素。那其實就跟block水平元素自動適應容器空間效果一模一樣了。除非你的容器寬度超過3000像素,實際上,一般web頁面不會有3000像素寬的模塊的。所以,要是你實在不放心,設個9999像素值好了!

    .float-left {
        float: left;
    }
    .bfc-content {
        display: table-cell; width: 9999px;
    }

    看上去,好像還不錯。但是,還是有兩點制約,一是IE8+以上瀏覽器兼容,有些苦逼的團隊還要管IE6;二是應付連續英文字符換行有些吃力(可以嵌套table-layout:fixed解決)。但是,總體來看,適用的場景要比overflow:hidden廣博很多。

  6. display:table-row 對width無感,無法自適應剩餘容器空間。
  7. display:table-caption 一無是處……還有其他聲明也都是一無是處,我就不全部展開了……

總結:我們對BFC聲明家族大致過了一遍,能擔任自適應佈局重任的也就是:

  1. overflow:auto/hidden IE7+
  2. display:inline-block IE6/IE7
  3. display:table-cell IE8+

由於overflow有剪裁和出現滾動條等隱患,不適合作爲整站通用類,於是,最後,類似清除浮動的通用類語句:

.clearfix {
    *zoom: 1;
}
.clearfix:after {
    content: ''; display: table; clear: both;
}

兩欄或多欄自適應佈局的通用類語句是(block水平標籤,需配合浮動):

.cell {
    display: table-cell; width: 9999px;
    *display: inline-block; *width: auto;
}

這就是zxx.lib.cssCSS樣式庫中.cell的由來!

當然,由於和浮動元素合作,清除浮動還是要的,於是,就有了.fix + .l/.r + .cell的無敵組合,可以多欄,也可以無限嵌套。

如果是局部,且確認安全;或有連續英文字符換行的隱患,你也可以使用.fix + .l/.r + .ovh的無敵組合,可以多欄,也可以無限嵌套。

四、結束之言

估計本文是春節前的最後一篇文章了,小生在這裏提前祝大家「羊年快樂」「萬事如意」「事業蒸蒸日上」!

另,本文內容非權威,多個人理解與感悟,僅供參考。歡迎交流,提出您的真知灼見!

感謝閱讀!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章