簡(jiǎn)書(shū)的Markdown貌似不支持插入iframe,所以文章里的JSFiddle示例都改做截圖了,如果有需要可以點(diǎn)擊下面的鏈接,到眾成查看,英文版鏈接一并附上。
譯者:kangflict(飛揚(yáng))
鏈接:http://www.zcfy.cc/article/966
原文:https://bocoup.com/weblog/dive-into-flexbox
簡(jiǎn)介
Flexbox 是 CSS3 中提出的新布局模型,用于適應(yīng)現(xiàn)代網(wǎng)絡(luò)開(kāi)發(fā)中日益復(fù)雜的布局需求。 近來(lái),F(xiàn)lexbox 的語(yǔ)法逐漸穩(wěn)定下來(lái),本文來(lái)為大家揭開(kāi)它的神秘面紗,并盤(pán)點(diǎn)其技術(shù)細(xì)節(jié)。瀏覽器支持將會(huì)快速增長(zhǎng),因此當(dāng)對(duì) Flexbox 的支持足夠廣泛以至達(dá)到實(shí)用水平時(shí),你將會(huì)在這場(chǎng)游戲當(dāng)中拔得頭籌。如果想要了解它的工作方式和原理,那么就繼續(xù)讀下去吧!
為什么需要 Flexbox?
長(zhǎng)期以來(lái),人們使用 table、float、inline-block 以及其他 CSS 特性來(lái)對(duì)站點(diǎn)內(nèi)容進(jìn)行布局。然而,這些工具沒(méi)有一個(gè)是為當(dāng)前的復(fù)雜網(wǎng)頁(yè)或網(wǎng)絡(luò)應(yīng)用設(shè)計(jì)的。像垂直居中這樣的簡(jiǎn)單的需求都需要費(fèi)點(diǎn)兒力氣才能滿(mǎn)足,以至于我們通常認(rèn)為單槍匹馬實(shí)現(xiàn)靈活柵格布局這樣的需求不太現(xiàn)實(shí),因此各種 CSS 柵格布局框架獲得了巨大的成功。
規(guī)范進(jìn)展與瀏覽器支持
Flexbox 規(guī)范的相關(guān)工作已經(jīng)進(jìn)行 3 年之久,很多瀏覽器廠商都發(fā)布了其實(shí)驗(yàn)性質(zhì)的實(shí)現(xiàn)。2012 年 9 月,針對(duì) Flexbox 語(yǔ)法的第三次主要修訂進(jìn)入了 W3C 的候選推薦階段。這說(shuō)明 W3C 對(duì)當(dāng)前的語(yǔ)法是滿(mǎn)意的,并鼓勵(lì)瀏覽器廠商進(jìn)行實(shí)現(xiàn)。
Flexbox 規(guī)范大事年表:
- 2009 年 7 月,工作草案(display: box;)
- 2011 年 3 月,工作草案(display: flexbox;)
- 2011 年 11 月,工作草案(display: flexbox;)
- 2012 年 3 月,工作草案(display: flexbox;)
- 2012 年 6 月,工作草案(display: flex;)
- 2012 年 9 月,候選推薦(display: flexe;)
各個(gè)瀏覽器都在積極地接納 Flexbox。本文寫(xiě)就之時(shí),Chrome 22+、Opera 12.1+ 以及 Opera Mobile 12.1+ 均已支持 Flexbox。Firefox 18 和 Blackberry 10 也將馬上跟進(jìn)。我建議使用支持 Flexbox 的瀏覽器來(lái)閱讀本文,以便觀察示例如何工作。
相關(guān)概念和術(shù)語(yǔ)
盡管在 Flexbox 的幫助下,以前千辛萬(wàn)苦才能實(shí)現(xiàn)或者根本不敢想象的布局都能輕而易舉地實(shí)現(xiàn),但要真正習(xí)慣以 Flexbox 的方式進(jìn)行布局還是需要一定的時(shí)間。在使用 Flexbox 時(shí),新的術(shù)語(yǔ)或概念都可能成為攔路虎,因此我們先來(lái)討論一下術(shù)語(yǔ)和概念的問(wèn)題。
Flexbox 布局涉及 Flex 容器(Flex Container)和 Flex 項(xiàng)目(Flex Item)。Flex 容器的 display 屬性為 flex 或 flexbox。flex 屬性使得容器渲染為塊級(jí)元素,而 inline-flex 則使其渲染為行內(nèi)元素。
下面是聲明 Flex 容器的示例:
.flex-container {
display: -webkit-flex;
display: flex;
}
本文中的所有代碼示例都將包含全面的瀏覽器廠商前綴。
Flex 容器的任何子元素都是 Flex 項(xiàng)目,并且 Flex 項(xiàng)目可以有任意多個(gè)。Flex 容器之外的任何元素以及 Flex 項(xiàng)目?jī)?nèi)的元素都將照常渲染。簡(jiǎn)而言之,F(xiàn)lex 容器決定 Flex 項(xiàng)目在其內(nèi)部的布局方式。
Flex 布局線(xiàn)(Flex Line)
在Flex容器內(nèi),F(xiàn)lex 項(xiàng)目沿 Flex 布局線(xiàn) 排布。默認(rèn)情況下,每個(gè)容器只有一條 Flex 布局線(xiàn)。

上圖的示例展示了默認(rèn)情況下兩個(gè)項(xiàng)目在容器中的排布:從左到右,沿水平 Flex 布局線(xiàn)排列。

書(shū)寫(xiě)模式(Writing Modes)
使用 Flexbox 進(jìn)行富有創(chuàng)造性的布局不可避免地要改變 Flex 布局線(xiàn)的方向。默認(rèn)情況下,F(xiàn)lex 布局線(xiàn)與文本書(shū)寫(xiě)方向一致:從左到右,從上到下。
W3C 有一個(gè)工作草案,是關(guān)于一項(xiàng)稱(chēng)作書(shū)寫(xiě)模式(Writing Modes)的新特性的。書(shū)寫(xiě)模式為從右至左或縱向的文字排布提供了新的實(shí)現(xiàn)方式,在對(duì)特定的語(yǔ)言進(jìn)行排版時(shí)常會(huì)遇到這樣的需求。
書(shū)寫(xiě)模式仍然處于半成品狀態(tài),不過(guò) direction 屬性已經(jīng)在 Chrome 中得到了支持。上面的示例中,如果如果我們將 direction 的值設(shè)置為 rtl (從右到左),那么不光文本內(nèi)容會(huì)從右往左渲染,F(xiàn)lex 布局線(xiàn)的方向也會(huì)改變,從而影響頁(yè)面的布局。

這或許就是很多與 Flexbox 相關(guān)的術(shù)語(yǔ)非常抽象的原因。如果不能確定頁(yè)面語(yǔ)言的話(huà),我們就不能簡(jiǎn)單地用“上”“下”“左”“右”來(lái)代表布局方向。
主軸與側(cè)軸(Main Axis and the Cross Axis)
Flexbox 通過(guò)引入概念主軸(Main Axis)和側(cè)軸(Cross Axis)來(lái)體現(xiàn)對(duì)書(shū)寫(xiě)模式的支持。Flex 布局線(xiàn)與主軸的方向一致,而側(cè)軸則與主軸正交。

每條軸的起點(diǎn)、終點(diǎn)以及方向的名字如下所示:
- 主軸起點(diǎn)(Main Start)
- 主軸終點(diǎn)(Main End)
- 主軸方向(Main Direction,有時(shí)也稱(chēng)作流方向,即 Flow Direction)
- 側(cè)軸起點(diǎn)(Cross Start)
- 側(cè)軸終點(diǎn)(Cross End)
- 側(cè)軸方向 (Cross Direction)
主軸和側(cè)軸兩個(gè)術(shù)語(yǔ)非常重要,我們有必要在繼續(xù)之前將其徹底搞懂。在使用 Flexbox 進(jìn)行布局的時(shí)候,任何東西都與此有關(guān)。本文所有的示例中,書(shū)寫(xiě)模式都是從左往右從上往下的,但是我們必須了解并不一定所有情況下都是如此。
Flex 容器之屬性
flex-direction
flex-direction 能夠改變 Flex 容器的軸向。flex-direction 的默認(rèn)值為 row,即按照 writing-mode 的方向?qū)?Flex 項(xiàng)目進(jìn)行排布,默認(rèn)情況下從左到右,從上往下。其他可能的值如下:
- row-reverse:主軸起點(diǎn)和終點(diǎn)對(duì)換。如果書(shū)寫(xiě)模式(writing-mode)從左往右,那么此時(shí) Flex 項(xiàng)目會(huì)由右往左進(jìn)行排布;
- column:主軸與側(cè)軸對(duì)換。如果書(shū)寫(xiě)系統(tǒng)為水平方向的,那么 Flex 項(xiàng)目將沿縱向排布;
- column-reverse:與column的效果相同,只是軸向翻轉(zhuǎn)。
將前面示例中的 flex-direction 值改為 column。

現(xiàn)在,F(xiàn)lex 項(xiàng)目變?yōu)榭v向排列的了。
justify-content
Flex 容器的 justify-content 屬性用于控制 Flex 項(xiàng)目在主軸上的位置。可能的值有:
- flex-start(默認(rèn)值)
- flex-end
- center
- space-between
- space-around
將 justify-content 的值設(shè)置為 center 能夠讓 Flex 項(xiàng)目沿主軸居中:

flex-start、flex-end 以及 center 的含義相當(dāng)直截,但是 space-between 和 space-around 這兩個(gè)用于確定 Flex 項(xiàng)目間留白的屬性值的含義就不那么明顯了。下面這張來(lái)自規(guī)范的示意圖對(duì)各個(gè)屬性值做出了最佳詮釋?zhuān)?/p>
align-items
align-items 是對(duì) justify-content 的補(bǔ)充,它確定的是 Flex 項(xiàng)目在側(cè)軸上的位置。可能的取值有:
- flex-start(默認(rèn)值)
- flex-end
- center
- baseline
- stretch
我們將 align-items 的值設(shè)置為 center,從而讓 Flex 項(xiàng)目在側(cè)軸居中排布:

flex-start、flex-end 和 center 的含義仍舊直截了當(dāng),strech 也相當(dāng)簡(jiǎn)單:讓 Flex 項(xiàng)目充滿(mǎn)從側(cè)軸起點(diǎn)到側(cè)軸終點(diǎn)的所有空間。baseline 則使得 Flex 項(xiàng)目沿基線(xiàn)對(duì)齊?;€(xiàn)是由 Flex 項(xiàng)目的內(nèi)容計(jì)算得出的。下面出自 Flexbox 規(guī)范的示意圖對(duì)此做出了最佳闡釋?zhuān)?/p>
flex-wrap
到現(xiàn)在為止,每個(gè) Flex 容器都只有一條 Flex 布局線(xiàn),利用 flex-wrap 屬性,就可以得到有多條 Flex 布局線(xiàn)的的容器。flex-wrap 可選的值如下:
- nowrap(默認(rèn)值)
- wrap
- wrap-reverse
如果 flex-wrap 的值為 wrap,那么當(dāng)一條 Flex 布局線(xiàn)無(wú)法容納所有 Flex 項(xiàng)目時(shí),多余的項(xiàng)目就會(huì)折轉(zhuǎn)到附加的 Flex 布局線(xiàn)上。新添的 Flex 布局線(xiàn)沿側(cè)軸的方向排列。
這里有一個(gè)使用 flex-wrap 的示例:

wrap-reverse 的效果與 wrap 相同,只是 Flex 布局線(xiàn)將沿與側(cè)軸相反的方向添加。
align-content
align-content 能夠影響 flex-wrap 的行為。它與 align-items 類(lèi)似,但是并不是用于對(duì)齊 Flex 項(xiàng)目,而是用于對(duì)齊 Flex 布局線(xiàn)。正如你所想,它們可能的取值也非常相似:
- stretch(默認(rèn)值)
- flex-start
- flex-end
- center
- space-between
- space-around
這些值的效果與它們?cè)?align-items 中的表親基本相同。
這里,我們將 align-content 設(shè)為 center:

flex-flow
flex-flow 是 flex-direction 和 flex-wrap 的簡(jiǎn)寫(xiě):
flex-flow: [flex-direction] [flex-wrap]
例如:
gistfile1.css
.flex-container {
-webkit-flex-flow: column nowrap;
flex-flow: column nowrap;
}
Flex 項(xiàng)目之屬性
Flex 項(xiàng)目是 Flex 容器的直接子元素,F(xiàn)lex 容器中的文字段落也會(huì)被當(dāng)做 Flex 項(xiàng)目處理。
Flex 項(xiàng)目的內(nèi)容則會(huì)照常渲染。例如,float 屬性對(duì) Flex 項(xiàng)目不起作用,但是 Flex 項(xiàng)目?jī)?nèi)部卻可以存在浮動(dòng)元素。
我們稱(chēng) Flex 項(xiàng)目有主軸尺寸和側(cè)軸尺寸。主軸尺寸是 Flex 項(xiàng)目在主軸方向上的尺寸,側(cè)軸尺寸則是 Flex 項(xiàng)目在側(cè)軸方向上的尺寸。事實(shí)上,通常 Flex 項(xiàng)目的寬和高分別就是它的主軸尺寸和側(cè)軸尺寸,不過(guò)這取決于 Flex 容器的軸的設(shè)置。
下面的屬性能夠影響 Flex 項(xiàng)目的行為。
order
order 最為簡(jiǎn)單,它控制著 Flex 項(xiàng)目的渲染順序。本例中,我們將一個(gè) Flex 項(xiàng)目的 order 屬性設(shè)置為 -1,從而使其顯示在所有其他元素之前。

這對(duì)可訪(fǎng)問(wèn)性來(lái)說(shuō)可能非常有用,因?yàn)橛袝r(shí)我們需要文檔結(jié)構(gòu)和頁(yè)面展示以不同的順序出現(xiàn)。
margin
你或許已經(jīng)對(duì) margin: auto; 在通常情況下的效果非常熟悉了。在 Flexbox 的世界里,它仍然發(fā)揮著相似的作用,只不過(guò)更加的強(qiáng)大?!白詣?dòng)”邊距能夠吸收掉多余的空白,從而將 Flex 項(xiàng)目推到不同的位置上去。
這里,我們?yōu)榈谝粋€(gè) Flex 項(xiàng)目設(shè)定 margin-right: auto;,從而使得該項(xiàng)目右邊所有多余的空白都被吸收掉:

接著,我們使用 margin: auto; 來(lái)摘取 CSS 排版的圣杯:真正的垂直居中!

align-self
Flex 項(xiàng)目的 align-self 屬性能夠覆蓋 Flex 容器為其指定的 align-items 屬性。兩個(gè)屬性具有相同的取值范圍:
- stretch(默認(rèn))
- flex-start
- flex-end
- center
- baseline
此例中,我們?yōu)槊總€(gè) Flex 項(xiàng)目指定了不同的 align-self 值:

上例中有兩個(gè) Flex 項(xiàng)目的 align-items 值為 baseline,這是因?yàn)?baseline 項(xiàng)目是以彼此為參照來(lái)進(jìn)行對(duì)齊的。
flex
終于到 Flexbox 中的 flex 屬性了。flex 確定了 Flex 項(xiàng)目在主軸剩余空間排布時(shí)的策略。
讓我們來(lái)逐一過(guò)目 flex 的常見(jiàn)取值:
flex: [數(shù)字]
該語(yǔ)法通過(guò)指定一個(gè)數(shù)字,來(lái)確定當(dāng)前 Flex 項(xiàng)目占主軸剩余空間的比例。
本例中,第一個(gè) Flex 項(xiàng)目占剩余空間的 2/4,其余兩個(gè) Flex 項(xiàng)目各占 1/4:

將每個(gè)項(xiàng)目的 flex 屬性都設(shè)置為 1 是一項(xiàng)非常有用的技巧,此時(shí)主軸剩余空間將被平均分配給各個(gè) Flex 項(xiàng)目。

flex: initial
flex 屬性為 initial 的項(xiàng)目將會(huì)失去彈性伸縮能力,但是在必要的時(shí)候仍然能夠縮?。ū粩D小)。
flex: auto
flex 屬性為 auto 的項(xiàng)目擁有在主軸上進(jìn)行彈性伸縮的所有能力。
目前,Opera 12.11 能夠支持 auto,但是 Chrome 23.0.1271.95 還不可以。實(shí)際上,我們只需要使用 flex: 1 就可以繞過(guò)這個(gè)問(wèn)題。
flex: none
flex 屬性為 none 的項(xiàng)目在任何情況下都不具備彈性伸縮的能力。
flex 進(jìn)階
事實(shí)上,flex 是 flex-grow、flex-shrink 和 flex-basis 的簡(jiǎn)寫(xiě):
flex: [flex-grow] [flex-shrink] [flex-basis]
不過(guò),多數(shù)情況下我們都不需要這樣寫(xiě),它需要對(duì) flex 背后的算法更為精深的理解。如果你覺(jué)得自己是真正的猛士,看這里的規(guī)范。
我們也可以為 flex-grow、flew-shrink 以及 flex-basis 分別指定屬性值。但是我強(qiáng)烈建議你不要這樣做,因?yàn)楫?dāng)使用 flex 簡(jiǎn)寫(xiě)形式時(shí),瀏覽器會(huì)為沒(méi)有值的屬性指定更有意義的默認(rèn)值。
visibility
如果有實(shí)現(xiàn)支持的話(huà),對(duì)于 Flex 項(xiàng)目,visibility: collapse; 應(yīng)當(dāng)表現(xiàn)得與 visibility: hidden; 和 display: none; 不同。具有該屬性的 Flex 項(xiàng)目將會(huì)影響所在 Flex 容器的側(cè)軸尺寸,但是并不占據(jù)主軸方向的空間,也不可見(jiàn)。這對(duì)動(dòng)態(tài)添加移除 Flex 項(xiàng)目非常有用,同時(shí)也不影響 Flex 容器的側(cè)軸尺寸。
目前,visibility: collapse; 貌似在任何瀏覽器上都沒(méi)有得到正確的支持。當(dāng)前的實(shí)現(xiàn)中,visibility: collapse; 的行為看上去與 visibility: hidden; 相同。我期待這種情況趕緊得以改觀。
這里有一個(gè)還不錯(cuò)的 collapse 模擬實(shí)現(xiàn),能夠讓你看到規(guī)范中 collapse 是如何工作的。
總結(jié)
如你所見(jiàn),F(xiàn)lexbox 是一種強(qiáng)大的新式布局工具,將從根本上變革我們現(xiàn)有的網(wǎng)站布局手段。亦如所見(jiàn),它需要我們使用全新的思考方式來(lái)進(jìn)行布局。我希望這篇文章能夠在你開(kāi)始使用 Flexbox 對(duì)網(wǎng)站進(jìn)行布局時(shí)幫你一把。不知道你信不信,反正我是看到了光明的未來(lái)。