多列布局在一個網(wǎng)頁設(shè)計中非常常見,不僅可以用來做外部容器的布局,在一些局部也經(jīng)常出現(xiàn)多列布局,比如下面圈出來的都是多列布局:

定寬 + 自適應(yīng)
定寬 | 自適應(yīng)
我們先講一個最簡單的兩列布局,左邊列定寬,右邊列自適應(yīng):

下面我們來看看有哪些方法可以解決這個問題:
float + margin
我們有如下html代碼:
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>
當我們沒有給他設(shè)置樣式的時候,它是這樣子

我們要的是兩列布局,所以我們給left加一個float:left;,然后它變成這樣了:

我們看到right的內(nèi)容環(huán)繞了left,這是浮動(float)的一個特性,那怎么解決呢? 因為右邊環(huán)繞了左邊,我們只需要將右邊往右邊移過去就行了: margin-left: 100px;如果左右兩列還想要一點間距,margin-left設(shè)置大一點就行了。

float + overflow
這個方案和前面的float + margin的方案很像,只是解決右邊環(huán)繞左邊的思路不一樣,我們先給左邊寫float:left;右邊還是會環(huán)繞左邊:

這次我們解決這個問題不用margin-left了,而是用overflow:

這種方案如果要間距,可以在left上設(shè)置一個margin-right: 20px;。為什么overflow:hidden可以決絕浮動環(huán)繞的問題呢,這其實用到了BFC的原理。下面我們來講講BFC:
BFC
BFC(Block Formatting Context)塊級格式上下文,是Web頁面中盒模型布局的CSS渲染模式,指一個獨立的渲染區(qū)域或者說是一個隔離的獨立容器。
下列情況都可以形成一個BFC:
1\. 浮動元素,float 除 none 以外的值;
2\. 定位元素,position(absolute,fixed);
3\. display 為以下其中之一的值 inline-block,table-cell,table-caption;
4\. overflow 除了 visible 以外的值(hidden,auto,scroll);
BFC有如下特性:
1\. 內(nèi)部的Box會在垂直方向上一個接一個的放置。
2\. 垂直方向上的距離由margin決定
3\. bfc的區(qū)域不會與float的元素區(qū)域重疊。
4\. 計算bfc的高度時,浮動元素也參與計算
5\. bfc就是頁面上的一個獨立容器,容器里面的子元素不會影響外面元素。
上面幾個特性怎么理解呢?
-
如果垂直方向上有多個div,他們都有margin,那垂直的margin會合并
<div class="parent"> <div class="child"></div> <div class="child"></div> </div> .child { margin-top: 10px; margin-bottom: 20px; }上述代碼兩個child之間的間距是20px,而不是30px,因為垂直的margin會合并。但如果我給上面第一個child再套一個容器,使用
overflow:hidden;他就成了一個BFC,根據(jù)BFC的特性,BFC的子元素不會影響外面的元素,margin就不會合并,兩個child的間距就是30px;<div class="parent3"> <div class="overflow"> <div class="child3">child4</div> </div> <div class="child3">child4</div> </div> .child3 { margin-top: 10px; margin-bottom: 20px; } .overflow { overflow: hidden; } BFC是一個獨立的容器,不會被浮動元素覆蓋,里面的文字也不會環(huán)繞浮動元素,我們這里的兩欄布局就是利用的這個特性。
-
計算BFC高度時,浮動元素的高度也會計算其中,這不就是我們用來清除浮動的一種做法嗎?
.parent { overflow: hidden; }
table
我們還可以用table來實現(xiàn),父級設(shè)置display為table,那他的寬度就是內(nèi)容的寬度,所以我們需要手動指定寬度為100%。兩個子級設(shè)置display為table-cell,這樣他們其實就相當于table的兩個單元格。由于我們要固定左邊的寬度,父級table應(yīng)該使用布局優(yōu)先,即table-layout: fixed;。這時候如果左右兩邊要間距,是沒法設(shè)置margin的,因為他們是單元格,但是我們可以在左邊子級上設(shè)置padding-right.
<div class="parent4">
<div class="left4">
<p>left4</p>
</div>
<div class="right4">
<p>right4</p>
<p>right4</p>
</div>
</div>
.parent4 {
display: table;
width: 100%;
table-layout: fixed;
}
.left4 {
display: table-cell;
width: 100px;
padding-right: 20px
}
.right4 {
display: table-cell;
}
table-layout
table-layout有兩個值:
- fixed: 是表格布局優(yōu)先,列寬由表格寬度和列寬度設(shè)定,而與單元格的內(nèi)容無關(guān)。這種模式下,瀏覽器在接收表格第一行后就可以渲染出來,速度更快。
- auto: 這是默認值,表示表格內(nèi)容優(yōu)先,列的寬度是由列單元格中沒有折行的最寬的內(nèi)容設(shè)定的。此算法有時會較慢,這是由于它需要在確定最終的布局之前訪問表格中所有的內(nèi)容。
flex
又遇到flex了,用flex做這種布局太簡單了,直接父級設(shè)置display: flex, 左子級定寬,右子級設(shè)置flex:1就行了,如果要間距,可以直接用margin。
.parent5 {
display: flex;
}
.left5 {
width: 100px;
margin-right: 20px;
}
.right5 {
flex: 1;
}
flex: 1
flex: 1是flex: 1 1 0的簡寫,對應(yīng)的完全體是:
{
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
}
-
flex-grow:設(shè)置元素的擴展比例。假如父級元素總寬度為500px, 子元素A, B, C三個元素的
flex-grow分別為1, 2, 2,那他們的寬度比例為1:2:2,三個元素最終的寬度為100px, 200px, 200px; -
flex-shrink: 設(shè)置元素的收縮比例。假如父級元素總寬度為500px,現(xiàn)在有A, B, C, D, E五個子元素,他們的
flex-shrink分別為1, 1, 1, 2, 2,他們的flex-basis都是120px。計算可知,五個子元素總寬度為120 *5 = 600px,超出了父級100px,所以需要對子元素進行收縮。收縮的時候就要通過flex-shrink來計算,我們發(fā)現(xiàn)他們flex-shrink的總和為1 + 1 + 1 + 2 + 2 = 7。所以將超出的100px分成7份,每份約14px,然后按照flex-shrink進行收縮。A, B ,C的份數(shù)都是1,所以他們收縮14px,他們的最終寬度是120 - 14 = 106px;D, E的份數(shù)是2,所以他們應(yīng)該收縮14 *2 = 28px,最終寬度是120 - 28 = 92px。 - flex-basis: 設(shè)置元素的初始值,擴展和收縮都以此為參照物。
定寬 | 定寬 | 自適應(yīng)
三列布局,前面兩列定寬,最后一列自適應(yīng),這個跟前面的一列定寬,一列自適應(yīng)的很像,很多方案都可以直接用, 比如用float + overflow。

不定寬 + 自適應(yīng)

兩列布局,左邊不定寬,寬度由內(nèi)容決定,右邊自適應(yīng)的常見解決方案:
不定寬:float + overflow
跟前面定寬的寫法很像,只是左邊子級寬度不能寫死了,要留給它的子元素決定。

不定寬:table
用table也可以實現(xiàn),但是要注意,table-layout不能設(shè)置fixed了,因為左邊寬度不定,我們可以不設(shè)置他,這樣就是默認值auto。默認的table天生寬度就是內(nèi)容決定的,左右兩邊如果內(nèi)容一樣長,那他們的長度可能是一樣的,都有留白,像這樣:

但是我們想要的是左邊擠到內(nèi)容區(qū),留白都給右邊,只需要給左邊一個很小的寬度,比如width: 0.1%或者1px都行。

不定寬:flex
又是flex,跟之前定寬的很像,只需要把前面左邊的寬度去掉就行了。
.parent5 {
display: flex;
}
.left5 {
margin-right: 20px;
}
.right5 {
flex: 1;
}
多列不定寬 + 自適應(yīng)

多列不定寬+自適應(yīng)前面幾種方案都可以實現(xiàn),以float + overflow為例:

等寬

等寬布局就是幾個元素,每個元素的寬度是一樣的,而且他們之間還可能有間距。如果沒有間距,這個很好實現(xiàn),每個元素寬度25%就行了,但是如果有間距,還設(shè)置25%,里面的內(nèi)容就超出父容器了,就會掉下來。那應(yīng)該怎么做呢?仔細看寫,我們會發(fā)現(xiàn)他們有如下關(guān)系:

C = W * N + G * (N -1); // 此處N為4
// 變換為
C = W * N + G * N - G;
// 再變?yōu)?C = (W + G) * N - G;
// 最后變?yōu)?C + G = (W + G) * N;
C + G = (W + G) * N;對應(yīng)的示意圖為:

這次我們的html結(jié)構(gòu)如下所示,間距是20px:
<div class="parent6">
<div class="column"><p>1</p></div>
<div class="column"><p>2</p></div>
<div class="column"><p>3</p></div>
<div class="column"><p>4</p></div>
</div>
等寬:float
通過前面的公式可知,我們需要將父級拓寬一個間距,即20px,用margin-left: 20px即可實現(xiàn)。每個子元素左浮動,寬度為25%,同時padding-left: 20px,這個是間距,我們?yōu)榱俗岄g距是在寬度內(nèi)部減出去,還需要設(shè)置box-sizing: border-box;。
.parent6 {
margin-left: -20px;
}
.column {
float: left;
width: 25%;
padding-left: 20px;
box-sizing: border-box;
}

用float的方式布局有一個不足之處,就是我們寫死了25%,這個只適用于4列,如果不知道幾列就不能這么寫了,當然用JS動態(tài)計算不算。
等寬:table
用table就不用寫死25%,因為在table-layout:fixed的情況下,列寬不是根據(jù)內(nèi)容計算的,默認列寬是相等的,天生就是等寬。但是在實現(xiàn)的時候需要注意,我們需要在parent外面再套一個容器,因為用table肯定會把parent設(shè)置成table,寬度是100%,沒辦法進行拓寬,再套一個容器的目的就是給他拓寬用的。

我們思考一下,如果不在parent外面再套一層容器能不能解決?當然是能解決的,在外面再套一層容器的目的無非就是拓寬parent寬度,我們可以直接指定parent寬度為calc(100% + 20px),這樣實際的內(nèi)容會靠右20px,我們再用相對定位左移20px就行了:
.parent8 {
display: table;
width: calc(100% + 20px);
table-layout: fixed;
position: relative;
left: -20px;
}
.column3 {
display: table-cell;
padding-left: 20px;
}
等寬:flex
用flex實現(xiàn)這個太簡單了,每個子元素都設(shè)置flex:1就行了。

等高
等高布局要實現(xiàn)的就是當一列高度被撐高時,另一列也會跟著被撐高。

等高:table
又是table,表格的一行里面不同的單元格天生就是等高的。

這個方案里面table-layut:fixed;可以不設(shè)置。間距用透明的border-right來做。background-clip是一個CSS3屬性,表示背景要顯示到的區(qū)域,有三個值:
- border-box: 背景被裁剪到邊框盒。
- padding-box: 背景被裁剪到內(nèi)邊距框。
- content-box: 背景被裁剪到內(nèi)容框。
等高:flex
萬能的flex又來了,也很簡單,跟前面定寬+自適應(yīng)的解決方案是一樣的。

這是因為flex默認的align-items就是stretch,就是拉伸到充滿容器。
等高:float
前面的布局解決方案里面都有float,等高能用float解決嗎?答案是可以的,但是稍微麻煩點。在前面定寬+自適應(yīng)的基礎(chǔ)上給左右子元素都寫一個極大的padding-bottom,這樣兩個子元素的高度都很大了,然后我們用一個同樣的大的負的margin-bottom和父級的overflow:hidden將高度減回來。

這樣做雖然左右子元素看起來是一樣高的,但是調(diào)試可以發(fā)現(xiàn),他們的高度已經(jīng)加了9999px,遠遠超過父容器了。這并不是真正意義上的等高,真正意義上的等高還是要用前面兩種方案。