Learning Objectives
- 說明 display: flex 加在父元素(容器)上,子元素就成為可以排成一列的項目
- 分辨主軸與交叉軸,並說明 flex-direction 怎麼改變主軸的方向
- 用 justify-content 控制項目在主軸上的位置與間隔,用 align-items 控制交叉軸上的對齊
- 用 flex-wrap 讓放不下的項目換行,並說明 align-content 什麼時候才有作用
- 用 gap 設定項目之間的距離,並寫出水平、垂直都置中的版面
把東西排成一排,曾經很麻煩
網頁上到處都是「排成一排」的東西:導覽列的連結、一排按鈕、一列卡片、頁首左邊的 logo 和右邊的登入鈕。
上一課你看過,區塊元素預設由上往下疊;想讓它們並排,過去要用 float 或 display: inline-block。
這兩種做法都能排成一列,但接下來的需求會很棘手:
| 想做的事 | 用 float、inline-block 時的困難 |
|---|---|
| 一排卡片高度相同 | 每張卡片的內容長短不一,高度自然不同 |
| 把一個方塊放在框框正中央 | 水平置中還好,垂直置中要靠各種技巧 |
| 空間不夠時自動換行,間距維持一致 | 要自己算 margin,最後一個項目還要特別處理 |
| 最後一個按鈕貼齊右邊 | 要另外浮動或定位 |
Flexbox(Flexible Box Layout,彈性盒子版面)就是為這類「沿著一個方向排一排東西」的工作設計的。 目前主流瀏覽器都支援,是現在排版的基本工具。這一課學怎麼排、怎麼對齊;下一課學項目怎麼伸縮分配空間。
容器、項目與兩條軸
Flexbox 只要在父元素加一行:
.container {
display: flex; /* 這個元素變成 flex 容器(flex container) */
}
它的直接子元素就自動變成 flex 項目(flex item),預設排成一列。孫元素不受影響,它們照舊排在自己的父元素裡。
Flexbox 的每個屬性都依兩條軸來說明:
- 主軸(main axis):項目排列的方向。預設由左往右。
- 交叉軸(cross axis):和主軸垂直的方向。預設由上往下。
主軸的方向由容器的 flex-direction 決定:
| flex-direction | 主軸 | 項目怎麼排 |
|---|---|---|
row(預設) |
水平,由左往右 | 排成一列 |
row-reverse |
水平,由右往左 | 排成一列,順序反過來 |
column |
垂直,由上往下 | 排成一欄 |
column-reverse |
垂直,由下往上 | 排成一欄,順序反過來 |
記住這件事,後面就不會搞混:justify-content 永遠管主軸,align-items 永遠管交叉軸。
主軸轉成直的,justify-content 就改成管垂直方向。
中文、英文由左往右書寫,所以
row是由左往右。如果頁面是阿拉伯文這類由右往左書寫的語言,row也會跟著由右往左。
justify-content 與 align-items
justify-content:主軸上還有空間時,項目放在哪裡、空間怎麼分。
| 值 | 效果 |
|---|---|
flex-start(預設) |
全部靠主軸的起點 |
flex-end |
全部靠主軸的終點 |
center |
全部擠在中間 |
space-between |
第一個貼起點、最後一個貼終點,剩下的空間平均分在項目之間 |
space-around |
每個項目左右各分到一樣的空間,所以兩端的空隙是中間的一半 |
space-evenly |
所有空隙(包含兩端)都一樣大 |
align-items:每個項目在交叉軸上怎麼對齊。
| 值 | 效果 |
|---|---|
stretch(預設的行為) |
項目拉長到和這一行一樣高(沒有設定高度時) |
flex-start/flex-end |
靠交叉軸的起點/終點 |
center |
在交叉軸上置中 |
baseline |
讓每個項目第一行文字的基線對齊,字級不同時很好用 |
在剛才的實驗裡,一開始 5 個方塊一樣高,就是因為 stretch:它們被拉到和容器一樣高。
這也是 Flexbox 做「等高卡片」的原理。
你也會看到
start、end這兩個值,它們來自較新的對齊規格,大多數情況和flex-start、flex-end效果相同。
完美置中:三行 CSS
「把一個東西放在框框正中央」曾經是網頁排版的經典難題。用 Flexbox 只要在外框寫三行:
.hero {
display: flex;
justify-content: center; /* 主軸(水平)置中 */
align-items: center; /* 交叉軸(垂直)置中 */
}
注意兩件事:
- 這三行寫在外框(容器)上,不是寫在要置中的卡片上。
- 外框要有高度,垂直置中才看得出來;高度和內容一樣時,上下沒有多的空間可以分。
小測驗:軸與對齊
容器設定了 flex-direction: column。這時 justify-content: center 會讓項目在哪個方向置中?
想讓 <nav> 裡的四個 <a> 排成一列,display: flex 應該寫在哪裡?
一列三個按鈕,希望所有空隙一樣大,包含最左邊和最右邊的空隙。應該用哪個值?
放不下時:flex-wrap、align-content 與 gap
Flexbox 預設 flex-wrap: nowrap:所有項目擠在同一行,寧可縮小(下一課會講縮小的規則),
縮到不能再縮就超出容器。標籤、按鈕這種數量不固定的東西,通常要讓它換行:
.tags {
display: flex;
flex-wrap: wrap; /* 放不下就換到下一行 */
gap: 8px; /* 項目之間留 8px,行與行之間也是 8px */
}
gap 只加在項目之間,不加在最外側,所以不用再為最後一個項目特別處理 margin。
要分開設定可以寫 gap: 12px 8px(第一個是行與行之間 row-gap,第二個是項目之間 column-gap)。
換行之後出現好幾行,這時才有 align-content:它決定這幾行在交叉軸上怎麼分配多出來的空間,
值和 justify-content 很像(flex-start、center、space-between…)。
| 屬性 | 管什麼 | 什麼時候有作用 |
|---|---|---|
align-items |
每一行裡面,項目在交叉軸上怎麼對齊 | 一直都有 |
align-content |
好幾行整體在交叉軸上怎麼排 | 只有 flex-wrap: wrap 而且真的換成多行時 |
只有一行(nowrap)的容器設定 align-content 不會有任何效果,這是很常見的疑問。
小測驗:換行、間距與置中
下面哪一種情況,設定 align-content: center 不會有任何效果?
要讓 .box 在 300px 高的 .wrap 裡水平、垂直都置中,最精簡的寫法是?
容器設定 gap: 10px,裡面一行有 4 個項目。項目之間的空隙一共有幾段 10px?
重點整理
display: flex寫在容器上,直接子元素變成 flex 項目。- 主軸是項目排列的方向,由
flex-direction決定;交叉軸和它垂直。 justify-content管主軸(起點、終點、置中、space-between/around/evenly);align-items管交叉軸(預設 stretch,所以項目等高)。flex-wrap: wrap讓放不下的項目換行;多行時才用align-content安排這幾行。- 項目之間的距離用
gap,它不會加在最外側。 - 完美置中:容器寫
display: flex; justify-content: center; align-items: center;,而且容器要有高度。
下一課講 Flexbox 的另一半:flex-grow、flex-shrink、flex-basis 怎麼把空間分給項目,以及導覽列、等高卡片、頁尾貼底這幾種常見版型。