前兩期講了position 布局和水平布局,這期接著之前的話題繼續(xù)聊聊更新一點的布局方式——Flexbox(彈性布局)。
Overview
Flexbox 也就是 Flexible Box Layout 模塊,是 CSS 提供的用于布局的一套新語法。這套布局包含針對容器(flex container)和針對其直接子元素(彈性項,flex item)的兩類方法。它可以控制的彈性特征包括:大小、流動方向、橫縱兩軸的排布、順序等等。Flex 已被 IE11 之后的瀏覽器完美支持,除了少數(shù)惡心的應(yīng)用,大家應(yīng)該放心地使用 flexbox;甚至應(yīng)該盡量避免使用傳統(tǒng)的內(nèi)容布局方式。
彈性布局是一個 display 屬性,使用時直接在容器上添加該屬性即可。還是以上一期的導(dǎo)航欄為例:
<ul>
<li><a href="/home">HOME</a></li>
<li><a href="/onion">Onion</a></li>
<li><a href="/garlic">Garlic</a></li>
</ul>

我們給容器 ul 加上 flex 屬性:
ul {
display: flex;
}

效果很明顯,彈性項<li>全部變成了類似于 inline-block 的元素了——水平布局顯現(xiàn)出來了。
Flex 方向:主軸和輔軸
Flexbox 可以針對某一區(qū)域控制其中元素的順序、大小、分布以及對齊。事實上這個區(qū)域的元素可以沿著兩個方向排列:
- 主軸(main axis):也就是橫軸,水平方向的排列
- 輔軸(cross axis):也就是縱軸,垂直方向的排列
默認(rèn)情況下子元素是依據(jù)主軸從左至右排布,也就是flex-direction: row;,但還有:row-reverse、 column、column-reverse等幾種排布。我們對比一下效果:

排布和我們的常識并不沖突,所以理解起來不難,
- row:從左往右排列
- row-reverse:從右往左排列
- column:從上至下排列
- column-reverse:從下至上排列
空間與對齊
上面提到了主軸和輔軸,flex 還可以對這兩個方向上的排布作出調(diào)整,這里需要記住兩個英語單詞:justify和align;從語義來說,前者翻譯過來是行(水平)排齊,后者是列(垂直)排齊。CSS 關(guān)鍵字里經(jīng)常出現(xiàn)這兩個單詞,大家記得區(qū)分語義。
justify-content
我們先看水平排布,它的屬性叫justify-content,主要有五種:flex-start、flex-end、center、space-between、space-around。
ul {
display: flex;
justify-content: flex-start;
}

默認(rèn)為 flex-start,表示左對齊;其他幾個也可顧名思義:右對齊、水平居中、居間留白、空間環(huán)繞。
這里順便提一個與 flex 有點關(guān)系的常規(guī)的布局:如何讓首元素(HOME)左對齊,剩余元素右對齊呢?

很簡單,讓首元素的右 margin 為 auto 即可,原理是:在 flex 布局里,外邊距 auto 會耗盡 flex 容器的所剩空間,剩余彈性項就這樣被前面的 margin 擠到了行尾。
ul li:first-child {
margin-right: auto;
}
同理,讓第二個元素的左 margin 為 auto 也可以實現(xiàn)上面一模一樣的效果:
ul li:nth-child(2) {
margin-left: auto;
}
再換一個布局,如何把上圖的首元素擠到右側(cè),剩余元素依舊左對齊呢?

這里需要用到 Flexbox 的 order 屬性,它是彈性項的屬性,可以幫助我們完全擺脫源碼中的順序約數(shù)。Order 的默認(rèn)值是 0,表示按源碼中的順序排列。我給首元素加一個很大的 order 值,如 999;瀏覽器發(fā)現(xiàn)它的 order 大于所有兄弟元素,就直接把它放到了行尾。然后我再把它的左 margin 設(shè)為 auto,首元素就被擠到最右側(cè)了。
ul li:first-child {
order: 999;
margin-left: auto;
}
Order 值不需要連續(xù),且可正可負(fù);只要可以比大小,相應(yīng)的項就可以按值升序排列了。
align-items
Flexbox 有水平對齊,自然也有垂直對齊,叫 align-itmes,是容器屬性,主要有四種: stretch、flex-start、center、flex-end。
ul {
display: flex;
align-items: stretch;
}
默認(rèn)值是stretch——高度自動拉伸;其他值不拉伸高度,分別是上中下對齊。上述示例中所有元素的高度是一致的,所以看不出垂直對齊的效果;下圖我特意把首元素的高度增加了一倍,大家就可以發(fā)現(xiàn)區(qū)別了:

align-items 屬性會對所有彈性項起效果,假如我只想調(diào)整末尾元素為居中對齊,那該怎么辦呢?

Flexbox 提供了一個叫 align-self 的屬性為彈性項調(diào)整個別項的對齊方式。我們只需要把 last-child 設(shè)成垂直居中即可:
li:last-child {
align-self: center;
}
這里順便提一下,flexbox 并沒有justify-self這個屬性,也就是說我們無法調(diào)整個別項的水平排布。嗯,輔軸功能并不見得比主軸弱。
伸縮
伸縮屬性也是用于調(diào)整個別項的排布,屬性名就叫flex
li:fist-child {
flex: 1 1 auto;
}
事實上,它是三個屬性的語法糖,按序分別是:
- flex-grow:用于拉伸的彈性系數(shù)(自然數(shù)),默認(rèn)值是 0
- flex-shrink:與 flex-grow 相反的彈性系數(shù),應(yīng)用于收縮狀態(tài),默認(rèn)值是 1
- flex-basis:在上面兩個屬性修正前的基準(zhǔn)大小
伸縮屬性有點難講,為了方便計算,我稍微調(diào)整了一下尺寸,順便把各項的尺寸顯示出來:
ul {
display: flex;
width: 24em;
}
li {
width: 4em;
}
flex-basis
flex-basis 的默認(rèn)值是 auto,但也可以是單位值(如 16px、1em),或是百分比(相對于主軸而言)。上面我事先寫死了彈性項<li>的寬度,這時候所有彈性項的默認(rèn) flex-basis 就等價于 4em 了。接著再為首元素單獨設(shè)置 flex-basis 為 8em:
li:first-child {
flex-basis: 8em;
}
我們比對一下設(shè)置前后的效果:

很明顯,首元素寬度變成了之前的兩倍。在未被伸縮屬性修正前,flex-basis 可以直接當(dāng)成寬度來用:若設(shè)置了 width 屬性,默認(rèn)值 auto 等于 width;若沒有設(shè)置 width,auto 又會根據(jù)內(nèi)容實際長度設(shè)置數(shù)值。當(dāng)然,flex-basis 并沒有這么簡單,我們接著往下看。
flex-grow
上圖中的容器<ul>還有剩余空間,我們不妨接著試試 flex-grow——為首元素加入伸展系數(shù):
li:first-child {
flex-basis: 8em;
flex-grow: 1;
}

在上述代碼的基礎(chǔ)上,試著給另一個彈性項 last-child 添加 flex-grow:
li:last-child {
flex-basis: 4em;
flex-grow: 1;
}

嗯,我們應(yīng)該可以看出一些端倪了:
- flex-grow 會拉伸彈性項,并最終填滿父元素的所有剩余空間
- 若多個彈性項同時設(shè)置了 flex-grow,它們會按一定的比例分配父元素的剩余空間
這里所謂的剩余空間 = 容器總長度 - 所有彈性項的 flex-basis 之和。
修正后,彈性項的實際長度 = 自身 flex-basis + 剩余空間 × 自身的 flex-grow / ∑ 所有彈性項的 flex-grow。
公式出來了,我很難再說得更清楚了。
flex-shrink
再看看收縮場景——當(dāng) flex 容器空間小于所有彈性項 flex-basis 之和時,flexbox 又會按一定比例壓縮各彈性項;flex-shrink 就是應(yīng)用于這個場景的收縮比例系數(shù)。
在收縮場景里,總壓縮長度(負(fù)空間) = 所有彈性項的 flex-basis 之和 - 容器總長度。
修正后,彈性項實際長度 = 自身 flex-basis - 總壓縮寬度 × [ 自身(flex-grow × flex-shrink) / ∑ 所有彈性項的(flex-grow × flex-shrink) ]。
flex-wrap
上面這個收縮計算有點麻煩了,而且也沒太必要計算出實際寬度。在很多的應(yīng)用場景里,我們會直接給容器加一個flex-wrap: wrap;屬性:當(dāng) flex 容器過窄時,彈性項直接換行即可。
ul {
display: flex;
flex-wrap: wrap;
}

這種根據(jù)寬度自動調(diào)整布局的設(shè)計,被稱為響應(yīng)式設(shè)計,這就是后話了。
小結(jié)
本文簡單介紹了 flexbox 最常用的幾個屬性,涵蓋了橫縱方向的排布、對齊、大小、次序調(diào)整等等方面。但是本文只是入門介紹,flexbox 的語法還有不少,具體實踐更遠(yuǎn)超本文描述,還是希望大家能繼續(xù)研讀 W3C 文檔。
相關(guān)
文章同步發(fā)布于an-Onion 的 Github。碼字不易,歡迎點贊。