- 原文地址:What Happens When You Create A Flexbox Flex Container?
- 原文作者:Rachel Andrew
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:linxuesia
- 校對者:tian-li, Moonliujk
快速簡介: 在我的理想世界裡,CSS Grid 和 Flexbox 應當作為一個整體一起出現,才能組成完整的網頁佈局系統。然而,我們先有了 Flexbox 屬性,因為它比浮動(floats)更適合用來做網格佈局,所以出現了很多以 Flexbox 為基礎的網格佈局系統。事實上,很多人覺得 Flexbox 彈性佈局令人困擾或者難以理解,都是因為嘗試把它作為網格佈局的方法。
在接下來的一系列文章裡面,我會花一點時間來詳細講解 Flexbox 彈性佈局 —— 以之前我用來理解 grid 屬性的方式。我們一起看看 Flexbox 設計是為了什麼,它擅長處理什麼,以及我們為什麼不選擇它作為佈局的方法。在這篇文章中,我們會詳細看一下,當你在樣式表新增 display: flex
的時候,究竟會發生什麼。
一個 Flex 容器,拜託了!
為了使用 Flexbox 佈局,你需要一個元素作為 flex 容器,在 CSS 中,使用 display: flex
:
HTML:
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
複製程式碼
CSS:
body {
padding: 20px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
* {box-sizing: border-box;}
p {
margin: 0 0 1em 0;
}
.container {
border: 5px solid rgb(111,41,97);
border-radius: .5em;
padding: 10px;
display: flex;
flex-direction: row-reverse;
}
.item {
width: 100px;
height: 100px;
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
複製程式碼
請看 Rachel Andrew(@rachelandrew)在 CodePen 上的這個例子:Smashing Flexbox Series 1: display: flex;。
讓我們花一點點時間來思考一下到底 display: flex
意味著什麼。在 Display Module Level 3 中,display 的每一個值都由兩個部分構成:內部顯示模式和外部顯示模式。當我們使用 display: flex
時,我們其實定義的是 display: block flex
。flex 容器的外部顯示模式是 block
,它在文件流中顯示為正常的塊級元素。內部顯示模式是 flex
,所以在容器內部的直接子元素按照彈性佈局來排列。
可能你之前沒仔細想過,但其實已經知道了。flex 容器在頁面中和其他塊級元素的表現一樣。如果你在 flex 容器後面緊跟一個段落,它們兩個都會表現為正常的塊級元素。
我們也可以把容器的屬性設定為 inline-flex
,就和設定成 display:inline flex
一樣。比如說有一個行內級別的 flex 容器,容器裡還有一些參與 flex 佈局的子元素。這個 flex 容器內的子元素的表現就和塊級 flex 容器內的子元素表現一樣,不同之處就在於容器本身在整體佈局中的表現。
HTML:
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
<em>這個 flex 容器是行內元素,所以另一個行內元素會緊跟它後面顯示。</em>
複製程式碼
CSS:
body {
padding: 20px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
* {box-sizing: border-box;}
p {
margin: 0 0 1em 0;
}
.container {
border: 5px solid rgb(111,41,97);
border-radius: .5em;
padding: 10px;
display: inline-flex;
}
.item {
width: 100px;
height: 100px;
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
複製程式碼
請看 Rachel Andrew(@rachelandrew)在 CodePen 上的這個例子:Smashing Flexbox Series 1: display: inline-flex;。
元素的外部顯示模式決定了它作為盒模型在頁面中怎樣顯示,同時與內部顯示模式一起,決定了其子元素的行為。這是一個很有用的思想。你可以把這種思想應用於任何 CSS 定義的盒模型。這個元素將會如何表現?它的子元素又會怎樣表現?這些問題的答案就與它們的內部顯示模式和外部顯示模式有關。
行或者列?
一旦我們定義了 flex 容器之後,一些預設值就開始發揮作用了。在我們沒有新增任何其他屬性的情況下,flex 子專案(flex item)會按照行來排列。這是因為 flex-direction
屬性的預設值就是 row
。如果你不對它進行設定,它就會按照行的方向來顯示。flex-direction
屬性是用來設定主軸(main axis)的排列方向,這個屬性還有其他的值:
column
row-reverse
column-reverse
當子專案排成一行的時候,子專案會按照在文件中的順序,從行內維度的起始邊緣依次排列。在規範中,這個邊緣就被叫做 main-start
。
main-start
是行內維度的起始位置(Large preview)。
如果我們使用 column
,子專案從塊級維度的起始邊緣開始排列,因此構成一列。
main-start
是塊級維度的起始位置(Large preview)。
當我們使用 row-reverse
時,main-start
和 main-end
的位置互換了,因此,子專案也會相應的按照相反的順序來排列。
main-start
在內聯維度的末尾(Large preview)。
column-reverse
也具有一樣的效果。另外還有一點很重要,那就是這些值並不會“改變子專案的順序”,儘管它們看起來是這樣的效果,它們改變的是這些子專案開始排列的位置:通過改變 main-start
來達到目的。所以我們的子專案會按照相反的方向來排列,這僅僅是因為它是從容器的結束位置開始排列的。
另外還有一件很重要的事情要記住,當排列順序發生改變時,這僅僅是視覺上的,因為我們要求子專案從結束位置開始排列。它們在文件中仍然是原來的順序,當你使用螢幕閱讀器的時候仍然是按照源文件的順序進行索引。如果你真的想要改變子元素的順序,不應該使用 row-reverse
,而是直接在文件源中去改變子專案的順序。
Flexbox 的兩條軸線
我們已經講解了 flexbox 的一個重要特性:能夠將主軸(main axis)的方向從行切換為列。這種軸的方向切換,就是為什麼我常常認為網格佈局中的對齊更容易理解的原因。因為在網格佈局中,在兩個方向上你都可以採用幾乎相同的方式來實現對齊。而對於彈性佈局來說會更麻煩點,因為在主軸(main axis)和交叉軸(cross axis)上,子專案的表現是不太相同的。
我們已經瞭解了主軸(main axis),即你用 flex-direction
屬性的值來定義的那根軸線。交叉軸(cross axis)則在另一個方向上。如果你設定 flex-direction: row
,那麼你的主軸(main axis)是沿著行的方向,你的交叉軸(cross axis)是沿著列的方向。如果設定 flex-direction: column
,主軸(main axis)是沿著列的方向,交叉軸(cross axis)是沿著行的方向。在這裡我們就需要討論 flexbox 的另外一個重要特點,那就是它與螢幕的物理方向無關。我們不討論從左到右方向的行,或從上到下方向的列,因為情況並非總是如此。
書寫模式
當我在上文中描述行和列的時候,我提到了塊級和行內的維度 dimensions。這篇文章是用英文寫的,它是水平的書寫模式。這就意味著當你要 Flexbox 顯示一行時,子專案會水平的展示。在這個例子中,main-start
位於左邊 —— 也就是英文書寫模式中中句子開始的位置。
如果我使用的是從右到左書寫的語言,比如阿拉伯語的話,起始位置就會位於右邊:
HTML:
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
複製程式碼
CSS:
body {
padding: 20px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
* {box-sizing: border-box;}
p {
margin: 0 0 1em 0;
}
.container {
border: 5px solid rgb(111,41,97);
border-radius: .5em;
padding: 10px;
display: flex;
flex-direction: row;
direction: rtl;
}
.item {
width: 100px;
height: 100px;
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
複製程式碼
請看 Rachel Andrew(@rachelandrew)在 CodePen 上的這個例子:Smashing Flexbox Series 1: row with rtl text。
flexbox 的初始值意味著,如果我所做的只是建立一個 flex 容器,我的子專案將會從右側開始顯示,並且向左排列。內聯方向的起始位置是你正在使用的書寫模式中句子開始的位置。
如果你使用垂直書寫模式並且使用的預設排列方向(這裡指 flex-direction: row),此時的行就會是垂直方向的,因為這就是垂直書寫方式語言排列行的方式。你可以嘗試為 flex 容器設定 writing-mode
屬性,把值設定為 vertical-lr
。現在,你再把 flex-direction
設定為 row
,子專案就會排成垂直的一列了。
HTML:
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
複製程式碼
CSS:
body {
padding: 20px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
* {box-sizing: border-box;}
p {
margin: 0 0 1em 0;
}
.container {
border: 5px solid rgb(111,41,97);
border-radius: .5em;
padding: 10px;
display: flex;
flex-direction: row;
writing-mode: vertical-lr;
}
.item {
width: 100px;
height: 100px;
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
複製程式碼
請看 Rachel Andrew(@rachelandrew)在 CodePen 上的這個例子:Smashing Flexbox Series 1: row with a vertical writing mode。
所以,一行可以水平的排列,main-start
位於左側或者右側,也可以垂直排列,main-start
位於頂部。即使我們的思維習慣了橫向排列的文字,很難想象一行垂直排列的文字,但它的 flex-direction
屬性的值仍然是 row
!
為了讓子專案按照塊級維度進行排列,我們可以把 flex-direction
的值設定成 column
或者 column-reverse
。在英語(或者阿拉伯語)這樣的水平書寫模式裡,子專案會從容器頂部開始按照垂直方向排列。
在垂直書寫模式中,塊級方向橫跨整個頁面,這也是這種書寫模式下塊級元素的排列方向。如果你將一列設定為為 vertical-lr
,那麼這些塊級元素會從左到右進行排列(內部的文字方向仍為垂直排列)。
HTML:
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
複製程式碼
CSS:
body {
padding: 20px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
* {box-sizing: border-box;}
p {
margin: 0 0 1em 0;
}
.container {
border: 5px solid rgb(111,41,97);
border-radius: .5em;
padding: 10px;
display: flex;
flex-direction: column;
writing-mode: vertical-lr;
}
.item {
width: 100px;
height: 100px;
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
複製程式碼
請看 Rachel Andrew(@rachelandrew)在 CodePen 上的這個例子:Smashing Flexbox Series 1: column in vertical-lr writing mode 。
但是,無論塊級元素怎麼顯示,只要你使用的是 column
方向,那麼元素始終處在塊級維度之中。
瞭解一行或者一列能夠在不同的物理方向上執行,有助於我們理解網格佈局和彈性佈局中的一些術語。在網格佈局和彈性佈局中,並不會使用『左和右』、『上和下』這樣的方向,因為我們並不會指定文件的書寫模式。現在所有的 CSS 都變的更注重書寫模式了。如果你對其他已經支援這種方向差異的 CSS 屬性和值有興趣的話,可以讀一下我的這篇文章 Logical Properties and Values。
總結一下,記住:
-
flex-direction: row
- 主軸 = 行內維度
main-start
位於當前書寫模式下句子開頭的位置- 交叉軸 = 塊級維度
-
flex-direction: column
- 主軸 = 塊級維度
main-start
位於當前書寫模式下塊級元素的開頭位置- 交叉軸 = 行內維度
預設對齊方式
當我們設定 display: flex
時,還會發生一些事情,預設的對齊方式會發揮作用。在該系列的其他文章中,我們會好好地瞭解一下對齊方式。但是,我們現在在探索 display: flex
的時候,也應該看一下這些發揮作用的預設值。
注意:值得注意的是,儘管這些對齊屬性始於 Flexbox 規範,但 Box Alignment(盒模型對齊)會最終覆蓋 Flexbox 規範的相關內容,如 flexbox 規範中所述。
主軸對齊方式
justify-content
屬性的預設值是 flex-start
,就像我們的 CSS 寫的那樣:
.container {
display: flex;
justify-content: flex-start;
}
複製程式碼
這就是我們的 flex 子專案(flex item)從 flex 容器的起始邊緣開始排列的原因。同樣也是當我們設定 row-reverse
時,它們變為從結束邊緣開始排列的原因,因為那個邊緣變成了主軸(main axis)的起點。
當你看見對齊屬性以 justify-
開頭時,這個屬性就是作用於主軸(main axis)的。也就是說 justify-content
屬性規定主軸(main axis)的對齊方式,並將子專案從起始邊緣對齊。
justify-content
還有其他的值:
flex-end
center
space-around
space-between
space-evenly
(在塊級對齊方式中新增)
這些值用來處理 flex 容器中剩餘空間的分佈。這就是子專案間會發生移動或者說相互分隔的原因了。如果你新增屬性 justify-content: space-between
,剩餘空間被平均分配給子專案。當然,這隻有在容器有剩餘空間的情況下才會發生。如果你的 flex 容器被全部充滿了(子專案排列完後,沒有任何剩餘空間),那麼設定 justify-content
屬性不會產生任何效果。
你可以把 flex-direction
設定成 column
。因為 flex 容器在沒有高度的情況下不會有剩餘空間,所以設定 justify-content: space-between
不會發生變化。如果你把容器設定成比展示所需要的高度更高的話,那麼這個屬性就會發揮作用了:
HTML:
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
複製程式碼
CSS:
body {
padding: 20px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
* {box-sizing: border-box;}
p {
margin: 0 0 1em 0;
}
.container {
border: 5px solid rgb(111,41,97);
border-radius: .5em;
padding: 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 500px;
}
.item {
width: 100px;
height: 100px;
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
複製程式碼
請看 Rachel Andrew(@rachelandrew)在 CodePen 上的這個例子:Smashing Flexbox Series 1: column with a height。
交叉軸的對齊方式
子專案在單一交叉軸的 flex 容器中也會沿著這根交叉軸對齊。這裡執行的對齊是子專案沿著交叉軸相互對齊。在接下來的這個例子中,有一個子專案比其他項佔據更高的空間,然後其他的子專案會按照某種規範來拉伸到與它相同的高度,這個規範就是 align-items
屬性,因為它的初始值就是 stretch
:
HTML:
<div class="container">
<div class="item">One</div>
<div class="item">Two</div>
<div class="item">
<ul>
<li>Three: a</li>
<li>Three: b</li>
<li>Three: c</li>
</ul>
</div>
</div>
複製程式碼
CSS:
body {
padding: 20px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
* {box-sizing: border-box;}
p {
margin: 0 0 1em 0;
}
.container {
border: 5px solid rgb(111,41,97);
border-radius: .5em;
padding: 10px;
display: flex;
}
.item {
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
.item ul {
margin: 0;
padding: 0;
list-style: none;
}
複製程式碼
請看 Rachel Andrew(@rachelandrew)在 CodePen 上的這個例子:Smashing Guide to Layout: clearfix。
當你在 flex 佈局中看到有一個屬性是以 align-
開頭的,那個就是交叉軸的對齊方式,align-items
屬性規定子專案在沿著交叉軸方向上的對齊方式,這個屬性的其他的值包括:
flex-start
flex-end
center
baseline
如果你不想其他的子專案跟拉伸到跟最高的那一項一樣高的話,設定 align-items: flex-start
,它會把子專案都沿著交叉軸的起始位置對齊。
HTML:
<div class="container">
<div class="item">One</div>
<div class="item">Two</div>
<div class="item">
<ul>
<li>Three: a</li>
<li>Three: b</li>
<li>Three: c</li>
</ul>
</div>
</div>
複製程式碼
CSS:
body {
padding: 20px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
* {box-sizing: border-box;}
p {
margin: 0 0 1em 0;
}
.container {
border: 5px solid rgb(111,41,97);
border-radius: .5em;
padding: 10px;
display: flex;
align-items: flex-start;
}
.item {
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
.item ul {
margin: 0;
padding: 0;
list-style: none;
}
複製程式碼
請看 Rachel Andrew(@rachelandrew)在 CodePen 上的這個例子:Smashing Flexbox Series 1: align-items: flex-start。
flex 子專案的初始值
終於說到這裡了,flex 子專案(flex item)也是有初始值的,它們包括:
flex-grow: 0
flex-shrink: 1
flex-basis: auto
這意味我們的子專案(flex item)在預設情況下不會自動充滿主軸上的剩餘空間。如果 flex-grow
被設定成一個正數,才會導致子專案拉伸並佔據剩餘空間。
這些子專案(flex item)同樣可以收縮。預設情況下,flex-shrink
的值被設定成了 1。這就意味著,如果我們的 flex 容器非常小,那麼其中的子元素在溢位容器之前就會自動的縮小以適應容器大小。這是一個非常靈活的屬性。總的來說就是,子專案在容器沒有足夠空間去排列的情況下依然能保持在容器之內,並且不會溢位。
為了在預設情況下獲得最好的展示效果,flex-basis
屬性的預設值被設定成 auto
,我們會在這個系列的其他文章中好好了解這代表什麼。現在,你只需要將 auto
理解為『大到足夠適應容器』就行了。在這種情況下,當 flex 容器中有一些子專案,其中的一個子專案相較於其他包含更多的內容,那麼它會被分配更多的空間。
HTML:
<div class="container">
<div class="item">Two words</div>
<div class="item">Now three words</div>
<div class="item">
This flex item has a lot of content and so it is going to need more space in the flex container.
</div>
</div>
複製程式碼
CSS:
body {
padding: 20px;
font: 1em Helvetica Neue, Helvetica, Arial, sans-serif;
}
* {box-sizing: border-box;}
p {
margin: 0 0 1em 0;
}
.container {
border: 5px solid rgb(111,41,97);
border-radius: .5em;
padding: 10px;
display: flex;
width: 400px;
}
.item {
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
複製程式碼
請看 Rachel Andrew(@rachelandrew)在 CodePen 上的這個例子:Smashing Flexbox Series 1: initial values of flex items。
上面這個例子就是彈性佈局靈活性的體現。在 flex-basis
屬性的預設值是 auto
,並且子專案(flex item)沒有設定尺寸的情況下,它就會有一個 max-content
的基礎尺寸。這就是它根據內容自動延伸之後沒有經過任何其他包裝的尺寸。然後,子專案按照比例來佔據剩餘空間,詳見以下 flexbox 規範中的說明。
『注意:分配收縮空間時,是根據基本尺寸乘以彈性收縮係數(flex shrink factor)。收縮空間的大小與子專案設定的
flex-shrink
的大小成比例。比如說有一個較小的子專案,在其他較大的子專案明顯收縮之前,是不會被收縮到沒有空間的。』
也就是說較大的子專案收縮的空間更多,那麼現在我們得到了最終的佈局結果。你可以比較一下下面兩個截圖,都是使用上面的例子,不同的是在第一個截圖中,第三個盒子內容較少佔據的空間較小,因此相對的每一列的佔據的空間更均勻一些。
其他項為了給較大的一項提供空間而自動收縮(Large preview)。
彈性佈局會試圖幫助我們獲得一個合理的最終顯示結果,而不需要寫 CSS 的人來定義。它不會平均的減少每行的寬度,從而形成一個每行只有幾個單詞的很高的子專案,而是會給該子專案分配更多的空間用以展示其內容。這種表現正是如何正確彈性佈局的關鍵。它最適用於用於沿著一條軸線排列的元素,以一種靈活和感知內容的方式。這裡我簡單介紹了一點細節,但在接下來的系列文章中我們會更加深入的瞭解這些演算法。
總結
在這篇文章中,我用彈性佈局的屬性的一些預設值來介紹當你設定 display: flex
的時候,究竟發生了什麼。令人驚訝的是,當你逐步分解之後,發現它原來有這麼多內容,並且這些內容就包含了彈性佈局的核心特點。
彈性佈局是非常靈活的:它會根據你的內容自動地做出不錯的選擇 —— 通過收縮和拉伸達到最好的展示效果。彈性佈局還能感知書寫模式:佈局中行和列的方向跟書寫模式有關。彈性佈局通過分配空間,允許子專案在主軸(main axis)上以整體的的方式來對齊。它還允許子專案按照交叉軸來對齊,使得交叉軸上的專案相互關聯。更重要的是,彈性佈局知道你的內容有多大,並且儘量採用更好的方式來展示你的內容。在接下來的文章中,我們會更加深入的探索,思考我們什麼時候以及為什麼要用彈性佈局。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。