- 原文地址:Everything You Need To Know About Alignment In Flexbox
- 原文作者:Rachel Andrew
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:CodeMing
- 校對者:Augustwuli、Ivocin
簡論: 在這篇文章中,我們將會在探討一些基本規則的同時談一談 Flexbox 的排列屬性以幫助我們知道橫軸和豎軸上(元素的)對齊是如何實現的。
在這個系列的第一篇文章中,我解釋了當你把一個元素設定為 display: flex
時發生了什麼。這次我們將會討論下排列屬性,同時也會討論(這些屬性)如何和 Flexbox 一起工作。如果你曾經對何時使用 align 屬性和 justify 屬性感到疑惑的話,我希望這篇文章將會讓(排列的)問題變得清晰! 如果你曾經對何時使用 align 屬性和 justify 屬性感到疑惑的話
Flexbox 排列方式的歷史
在整個 CSS 佈局的歷史中,如何能恰當地在橫豎兩軸正確排列元素估計是 Web 設計中最難的問題了。所以當瀏覽器中開始出現能夠在兩軸恰當地排列元素和元素組的 Flexbox 排列方式時,像你我一樣的廣大web開發者都為之激動不已。排列方式變得簡單到只需要兩行 CSS 程式碼:
HTML:
<div class="container">
<div class="item">Item</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;
height: 300px;
width: 300px;
justify-content: center;
align-items: center;
}
.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 2: center an item 。
你所瞭解的 flexbox 排列屬性目前都已經完整地被收錄到 盒子元素排列規範中了。這個規範文件詳細地說明了在各種佈局情況下的元素排列如何工作。這意味著我們在使用 Flexbox 排列方式或者將來在不同佈局情況下都可以在 CSS Grid 中使用相同的排列屬性。因此,任何新的排列都會在新的盒子元素排列規範中指出,而不是新的 Flexbox 版本。(譯者注:此處是新的特性是以新的排列屬性/方法來建立的,而不是更新 Flexbox 版本)
屬性
許多人告訴我他們在使用 flexbox 的時候很難區別是應該使用以 align-
還是 justify-
開頭的屬性。所以你需要知道:
justify-
實現主軸上的排列方式。即排列與你的flex-direction
相同的方向。align-
實現交叉軸上的排列方式。即排列與你的flex-direction
相垂直的方向。
在下文中,根據主軸和交叉軸而不是水平和垂直的方向來思考會更容易理解。(主軸和交叉軸)和物理方位一點關係都沒有。
用 justify-content 來排列主軸
我們將會從主軸排列來開始討論。在主軸上,我們通過 justify-content
屬性來實現排列。這個屬性的作用物件是我們的所有 flexbox 子元素所組成的組。同時也控制著組內所有元素的間距。
預設的 justify-content
值是 flex-start
。這也就是為什麼你宣告 display: flex
之後你的所有 flexbox 子元素朝著你的 flex 盒子的開始排成一行。如果你有一個值為 row
的 flex-direction
屬性同時頁面是從左到右讀的語言(例如英語)的話,這些字元素將會從左邊開始排列。
子元素從盒子的開始排列(放大預覽)
記住 justify-content
只會在 盒子有剩餘空間可以分配時 發揮作用。所以如果你的子元素佔滿了主軸的空間的話,justify-content
將不會產生任何作用。
盒子沒有任何剩餘空間可以分配(放大預覽)
如果將 justify-content
設定為 flex-end
的話,所有的元素將會移動到 flex 盒子的結束排成一行。空閒空間將會被移到 flex 盒子的開始之處。
子元素從盒子的結束排列(放大預覽)
我們可以對這些剩餘的區域做其他事情。我們可以通過 justify-content: space-between
來讓它分佈在 flex 盒子的兩個子元素之間。在這種情況下,最前和最後的兩個子元素會貼著容器,同時所有的空間將會被平均分配在每一個子元素之間。
剩餘空間分配在子元素之間(放大預覽)
我們也可以通過使用 justify-content: space-around
將這些空間環繞著 flex 盒子的子元素。在這種情況下,子元素將會均勻地分佈在容器中,同時可用空間也會被這些元素分享。
子元素的兩側都有空間([放大預覽(cloud.netlifyusercontent.com/assets/344d…))
在盒子排列規範中可以找到一個 justify-content
的更新的值,它沒有出現在 Flexbox 的規範中。它的值是space-evenly
。在這種情況下,子元素會均勻分佈在容器內,同時額外的空間將會被子元素的兩側所分享。
元素均勻地分佈在容器內(放大預覽)
你可以在 demo 中嘗試一下:
HTML:
<div class="controls">
<select id="justifyMe">
<option value="flex-start">flex-start</option>
<option value="flex-end">flex-end</option>
<option value="center">center</option>
<option value="space-around">space-around</option>
<option value="space-between">space-between</option>
<option value="space-evenly">space-evenly</option>
</select>
</div>
<div class="container" id="container">
<div class="item">One</div>
<div class="item">Two Two</div>
<div class="item">Three Three Three</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);
}
.controls {
background-color: rgba(0,0,0,.1);
padding: 10px;
border-radius: .5em;
border: 1px solid rgba(0,0,0,.2);
margin: 0 0 2em 0
}
.controls select {
font-size: .9em;
}複製程式碼
JavaScript:
var justify = document.getElementById("justifyMe");
justifyMe.addEventListener("change", function (evt) {
document.getElementById("container").style.justifyContent = evt.target.value;
});複製程式碼
可以看由 Rachel Andrew (@rachelandrew) 建立的在 CodePen 上的展示:Smashing Flexbox Series 2: justify-content with flex-direction: row
這些值同樣會在你的 flex-direction
為 column
時生效。你可能沒有額外的空間來分配一個列,但你可以通過新增 height 屬性或者是改變 flex 容器的大小來解決這個問題。就像下面這個 demo 一樣。
HTML:
<div class="controls">
<select id="justifyMe">
<option value="flex-start">flex-start</option>
<option value="flex-end">flex-end</option>
<option value="center">center</option>
<option value="space-around">space-around</option>
<option value="space-between">space-between</option>
<option value="space-evenly">space-evenly</option>
</select>
</div>
<div class="container" id="container">
<div class="item">One</div>
<div class="item">Two Two</div>
<div class="item">Three Three Three</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;
height: 60vh;
}
.item {
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
.controls {
background-color: rgba(0,0,0,.1);
padding: 10px;
border-radius: .5em;
border: 1px solid rgba(0,0,0,.2);
margin: 0 0 2em 0
}
.controls select {
font-size: .9em;
}複製程式碼
JavaScript:
var justify = document.getElementById("justifyMe");
justifyMe.addEventListener("change", function (evt) {
document.getElementById("container").style.justifyContent = evt.target.value;
});複製程式碼
可以看由 Rachel Andrew (@rachelandrew) 建立的在 CodePen 上的展示:Smashing Flexbox Series 2: justify-content with flex-direction: column
用 align-content 來排列交叉軸
如果你給你的 flex 容器新增了 flex-wrap: wrap
同時也有好幾條 flex 排列行,那你可以用 align-content
屬性來在交叉軸上排列你的 flex 排列行。不過,這將會需要交叉軸上有額外的空間。在下面這個 demo 中,我的交叉軸作為豎直的列在執行,同時我設定了這個 flex 容器的高度為 60vh
。由於這個高度比我展示 flex 子元素所需的高度大,所以我的容器有了交叉軸方向上的空餘空間。
我可以使用下面所有的 align-content
屬性值:
HTML:
<div class="controls">
<select id="alignMe">
<option value="stretch">stretch</option>
<option value="flex-start">flex-start</option>
<option value="flex-end">flex-end</option>
<option value="center">center</option>
<option value="space-around">space-around</option>
<option value="space-between">space-between</option>
<option value="space-evenly">space-evenly</option>
</select>
</div>
<div class="container" id="container">
<div class="item">One</div>
<div class="item">Two Two</div>
<div class="item">Three Three Three</div>
<div class="item">Four Four Four Four</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;
max-width: 400px;
height: 60vh;
display: flex;
flex-wrap: wrap;
}
.item {
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
.controls {
background-color: rgba(0,0,0,.1);
padding: 10px;
border-radius: .5em;
border: 1px solid rgba(0,0,0,.2);
margin: 0 0 2em 0
}
.controls select {
font-size: .9em;
}複製程式碼
JavaScript:
var align = document.getElementById("alignMe");
alignMe.addEventListener("change", function (evt) {
document.getElementById("container").style.alignContent = evt.target.value;
});複製程式碼
可以看由 Rachel Andrew (@rachelandrew) 建立的在 CodePen 上的展示:Smashing Flexbox Series 2: align-content with flex-direction: row
如果我的 flex-direction
值為 column
的話,那 align-content
屬性將像下面這個例子一樣執行:
HTML:
<div class="controls">
<select id="alignMe">
<option value="stretch">stretch</option>
<option value="flex-start">flex-start</option>
<option value="flex-end">flex-end</option>
<option value="center">center</option>
<option value="space-around">space-around</option>
<option value="space-between">space-between</option>
<option value="space-evenly">space-evenly</option>
</select>
</div>
<div class="container" id="container">
<div class="item">One</div>
<div class="item">Two Two</div>
<div class="item">Three Three Three</div>
<div class="item">Four Four Four Four</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;
max-width: 400px;
height: 60vh;
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
.item {
height: 20vh;
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
.controls {
background-color: rgba(0,0,0,.1);
padding: 10px;
border-radius: .5em;
border: 1px solid rgba(0,0,0,.2);
margin: 0 0 2em 0
}
.controls select {
font-size: .9em;
}複製程式碼
JacaScript:
var align = document.getElementById("alignMe");
alignMe.addEventListener("change", function (evt) {
document.getElementById("container").style.alignContent = evt.target.value;
});複製程式碼
可以看由 Rachel Andrew (@rachelandrew) 建立的在 CodePen 上的展示:Smashing Flexbox Series 2: align-content with flex-direction: column
正如 justify-content
屬性一樣,我們的設定作用在(子元素所組成的)子元素組中,同時空餘空間被分割。
簡寫方法 place-content
在盒子元素排列規範中,我們發現了一個簡寫方法 place-content
。使用這個屬性意味著你可以一次性設定 justify-content
和 align-content
。它的第一個值是 align-content
,第二個值是 align-content
。如果你僅僅設定了一個值 A,那麼這兩個值都將設定成 A,因此:
.container {
place-content: space-between stretch;
}
複製程式碼
和下面一樣:
.container {
align-content: space-between;
justify-content: stretch;
}
複製程式碼
如果我們使用:
.container {
place-content: space-between;
}
複製程式碼
那將和下面一樣:
.container {
align-content: space-between;
justify-content: space-between;
}
複製程式碼
用 align-items 來排列交叉軸
我們現在知道,我們可以對(所有 flex 子元素所組成的)子元素組進行關於 flex 元素和 flex 排列行的操作。不過,我們希望有其它方式即通過宣告元素與元素之間在數軸上的關係來操作我們的元素。你的 flex 容器有一個高度,這個高度可能是由容器內最高的子元素所決定的,就如下圖一樣。
容器的高度被第三個子元素所定義(放大預覽)
flex 容器的高度可以通過給 flex 容器新增一個 height 屬性所代替:
容器的高度通過該容器的大小屬性所定義(放大預覽)
flex 子元素看起來都被拉伸到最高的子元素的高度的原因是 align-items
的初始值是 stretch
。子元素們在交叉軸上被拉伸成 flex 容器在那個方向上的尺寸了。
請記住哪裡出現 align-items
會導致困惑:如果你有一個具有多個 flex 排列行的 flex 容器,那麼每一個 flex 排列行都會像一個新的 flex 容器一樣,(該行的)最高的 flex 子元素將會決定哪一行的所有 flex 子元素高度。
除了設定拉伸的初始值之外,你也可以給 align-items
屬性設定一個值 flex-start
,在這種情況下 flex 子元素將會在容器的開始之處排列同時也不會拉伸。
flex 子元素排列在交叉軸的開始之處(放大預覽)
設定值 flex-end
將會把它們(flex 子元素)移到交叉軸的結束之處。
flex 子元素排列在交叉軸的結束之處(放大預覽)
如果你使用值 center
,那 flex 子元素將會排列在交叉軸中央:
flex 子元素排列在交叉軸中央(放大預覽)
我們也可以設定依據文字基準線排列。這將會確保(flex 子元素)以文字的基準線排列,而不是盒子的邊框。
根據文字基準線排列(放大預覽)
你可以嘗試使用這個 demo 中的值:
HTML:
<div class="controls">
<select id="alignMe">
<option value="stretch">stretch</option>
<option value="flex-start">flex-start</option>
<option value="flex-end">flex-end</option>
<option value="center">center</option>
<option value="baseline">baseline</option>
</select>
</div>
<div class="container" id="container">
<div class="item">One</div>
<div class="item">Two Two</div>
<div class="item large">Three Three Three</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;
height: 40vh;
}
.item {
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
.large {
font-size: 150%;
}
.controls {
background-color: rgba(0,0,0,.1);
padding: 10px;
border-radius: .5em;
border: 1px solid rgba(0,0,0,.2);
margin: 0 0 2em 0
}
.controls select {
font-size: .9em;
}複製程式碼
JacaScript:
var align = document.getElementById("alignMe");
alignMe.addEventListener("change", function (evt) {
document.getElementById("container").style.alignItems = evt.target.value;
});複製程式碼
可以看由 Rachel Andrew (@rachelandrew) 建立的在 CodePen 上的展示:Smashing Flexbox Series 2: align-items
使用 align-self 來設定單個元素的排列
align-items
意味著你可以一次設定所有的 flex 子元素。這個操作的真正原理是對所有的 flex 子元素一一設定其 align-self
值。當然你也可以任意單一的 flex 子元素設定 align-self
值來使其與同一個 flex 容器的其它 flex 子元素不一樣。
在下面的例子中,我使用了 align-items
屬性來設定 flex 子元素組的排列方式是 center
,但是同時也給第一個和最後一個設定了 align-self
屬性來改變他們的排列方式。
HTML:
<div class="container" id="container">
<div class="item">One</div>
<div class="item">Two Two</div>
<div class="item">Three Three Three</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;
height: 40vh;
align-items: center;
}
.item {
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
.item:first-child {
align-self: flex-end;
}
.item:last-child {
align-self: stretch;
}複製程式碼
可以看由 Rachel Andrew (@rachelandrew) 建立的在 CodePen 上的展示: Smashing Flexbox Series 2: align-self
為什麼沒有 justify-self?
一個很常見的問題是為什麼不能在主軸上排列單個元素或元素組? 為什麼在主軸上沒有 -self
排列屬性?如果你認為 justify-content
和 align-content
的作用是關於空餘空間分佈的,那麼它們沒有單獨的排列方法的理由就顯而易見了。我們將flex 子元素作為一整個組進行處理,並以某種方式分配可用空間——在組的開頭及結尾或 flex 子元素之間。
想想 justify-content
和 align-content
在 CSS Gird 佈局中如何起作用也是很有幫助的。這兩個屬性用於描述在 gird 容器和 gird 塊之間的空餘空間如何分配。再次地,我們將 gird 塊當作一個組,然後這些屬性決定他們之間的所有額外空間。正如我們在 Gird 和 Flexbox 中展示的作用那樣,我們不能指定某一個元素去做一些不一樣的事情。不過,有一個方法可以實現你想要的在主軸上類似 self
屬性的佈局,那就是使用自動外邊距。
在主軸上使用自動外邊距
如果你曾經在 CSS 中將一個塊級元素居中(就像將頁面主元素的容器通過將它的左右外邊距設定為 auto
),那麼你就已經有了如何設定自動外邊距的經驗了。當一個外邊距的值設定為 auto 時,它(外邊距)會盡可能地嘗試在其所指的方向上變大。在使用外邊距將一個塊級元素居中時,我們將其左右的外邊距都設定為了 auto;它們(左右外邊距)都會盡可能地佔據空間於是就將塊級元素擠到了中間。
在 Flexbox 中使用自動外邊距來排列主軸上的單個元素或者一組元素的效果非常好。在下面的例子中,我們實現了一個共同的設計模式。我有一個使用 Flexbox 的導航欄,其子元素以行的形式排列同時使用了預設值 justify-content: start
。我想讓最後的那個子元素和其它子元素分開並展示在 flex 排列行的最後面——假設該行有足夠的空間。
我定位到了那個元素並且把它的 margin-left 屬性設定成了 auto。這意味著它的外邊距將會盡可能地佔用它左邊的空間,這意味著那個子元素被推到了最右邊。
HTML:
<div class="container" id="container">
<div class="item">One</div>
<div class="item">Two Two</div>
<div class="item push">Three Three Three</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);
}
.push {
margin-left: auto;
}複製程式碼
可以看由 Rachel Andrew (@rachelandrew) 建立的在 CodePen 上的展示: Smashing Flexbox Series 2: alignment with auto margins
如果你在主軸上使用了自動外邊距,那 justify-content
將不會有任何作用,因為自動外邊距將會佔據所有之前用在 justify-content
上的空間。
回退排列
每一個排列方法詳細來說都會有一個回退排列,它會說明在你請求的排列方式無法實現時會發生什麼。舉個例子,如果在你的 flex 容器中只有一個子元素但你宣告瞭 justify-content: space-between
,會發生什麼?答案是(該屬性的)回退排列 flex-start
會讓你唯一的那個子元素排列在 flex 容器的開始之處。對於 justify-content: space-around
,回退排列 center
將會被使用。
在現在的規範中你不能改變回退排列的值,所以如果你希望 space-between
的回退值是 center
而不是 flex-start
的話,並沒有方法能實現。這是 一份規範筆記,它描述了未來版本可能會支援這種方式。
安全和非安全的排列
最新的一個新增到盒子元素排列規範的是使用
和
關鍵詞的關於安全和非安全的排列的概念。
看下面的程式碼,最後一個元素相較於容器太寬了同時是 unsafe 排列並且 flex 容器是在頁面左邊的,當子元素溢位介面之外時,其被裁減了。
.container {
display: flex;
flex-direction: column;
width: 100px;
align-items: unsafe center;
}
.item:last-child {
width: 200px;
}
複製程式碼
不安全的排列將會按照你定義的排列但可能導致介面資料丟失(放大預覽)
安全的排列將會保護介面資料免於丟失,方式是通過重新移動溢位區間到其他地方:
.container {
display: flex;
flex-direction: column;
width: 100px;
align-items: safe center;
}
.item:last-child {
width: 200px;
}
複製程式碼
安全的排列會嘗試避免資料丟失(放大預覽)
這些關鍵詞現在還很少有瀏覽器支援(譯者注:自測 Chrome 69 已支援),不過,它展示了在盒子元素排列規範中帶給了 Flexbox 額外的控制方式。
HTML:
<div class="container" id="container">
<div class="item">One</div>
<div class="item">Two Two</div>
<div class="item">Three Three Three</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;
width: 100px;
align-items: safe center;
}
.item {
padding: 10px;
background-color: rgba(111,41,97,.3);
border: 2px solid rgba(111,41,97,.5);
}
.item:last-child {
width: 200px;
}複製程式碼
可以看由 Rachel Andrew (@rachelandrew) 建立的在 CodePen 上的展示: Smashing Flexbox Series 2: safe or unsafe alignment
總結
Flexbox 的排列屬性最初以列表的方式出現,但是現在它們有了自己的規範同時也適用於其它的佈局環境。這裡是一些小知識可能幫助你如何在 Flexbox 中使用它們:
justify-
適用於主軸,align-
適用於交叉軸;- 使用
align-content
和justify-content
時你需要空餘空間; align-content
和justify-content
屬性面向的是子元素組、作用是分享空間。因此,你不能指定一個特定的子元素同時它們也沒有對應-self
排列屬性;- 如果你想去排列一個子元素,或者在主軸上分離出一個組,請用自動外邊距實現;
align-items
屬性設定了整個子元素組的所有align-self
值。可以通過設定align-self
屬性來設定一個特定的子元素。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。