Flexbox

CSS Podcast - 010: Flexbox

在回應式設計中,有一種設計模式可能會造成困擾,那就是與部分內容併列的側欄。在可視區域有空間的情況下,這個模式非常實用,但在空間受限的情況下,這種固定版面配置可能會造成問題。

彈性方塊版面配置模型 (flexbox) 是一種為單維內容設計的版面配置模型。它擅長處理不同大小的項目,並為這些項目傳回最佳版面配置。

這是此側欄模式的理想版面配置模型。Flexbox 不僅可協助將側欄和內容放置在內文中,如果剩餘空間不足,側欄也會斷行至新行。使用彈性容器時,您可以提供彈性邊界,藉此提示瀏覽器如何顯示內容,而非設定瀏覽器必須遵循的硬性尺寸。

彈性版面配置有哪些用途?

Flex 版面配置提供下列功能,您可在本指南中探索這些功能。

  • 可顯示為資料列或資料欄。
  • 並會遵循文件的寫入模式。
  • 預設為單行,但可以要求換行為多行。
  • 版面配置中的項目可視覺重新排序,不受 DOM 中的順序影響。
  • 您可在項目內分配空間,讓空間根據父項中可用的空間調整大小。
  • 您可以使用「Box Alignment」屬性,在已折行的版面配置中,在項目和 Flex 行周圍分配空間。
  • 項目本身可在交錯軸上對齊。

主軸和交叉軸

要瞭解 Flexbox,關鍵在於瞭解主軸和交叉軸的概念。主要軸是 flex-direction 屬性所設定的軸。如果是 row,則主軸沿著資料列;如果是 column,則主軸沿著資料欄。

三個相鄰的方塊,箭頭從左向右指向。箭頭標示為「主軸」

Flex 項目會在主軸上以群組方式移動。請注意:我們有許多東西,並試圖為這些項目找出最佳版面配置。

交叉軸會沿著主軸的另一方向執行,因此如果 flex-directionrow,則交叉軸會沿著資料欄執行。

三個不同高度的方塊,彼此各有一支箭頭,指向左。箭頭標示為「主軸」。另一個箭頭則是從上到下。這是標有「跨軸」標籤的圖表

您可以在交叉軸上執行兩種操作。您可以個別移動項目,也可以將項目做為一組移動,讓項目與 Flex 容器對齊。此外,如果您已包裝 flex 行,可以將這些行視為群組,以控管為這些行指派的空間。您將在本指南中看到所有機制的實際運作方式,但目前請記得,主軸跟隨 flex-direction

建立 Flex 容器

讓我們用 Flexbox 取出一組不同大小的項目,並使用 Flexbox 進行版面配置,說明 Flexbox 的行為。

<div class="container" id="container">
  <div>One</div>
  <div>Item two</div>
  <div>The item we will refer to as three</div>
</div>

如要使用 Flexbox,您必須宣告要使用 Flex 格式結構定義,而非一般區塊和內嵌版面配置。方法是將 display 屬性的值變更為 flex

.container {
  display: flex;
}

如同版面配置指南所述,您將會看到包含彈性項目子項的區塊層級方塊。Flex 項目會使用其初始值,立即開始呈現一些 Flexbox 行為。

初始值的意思是:

  • 項目會以資料列的形式顯示。
  • 不會產生包裝。
  • 不會成長來填滿容器。
  • 它們會在容器的開頭對齊。

控制項目的方向

即使您尚未新增 flex-direction 屬性,項目仍會以一列顯示,因為 flex-direction 的初始值為 row。如果您想要一行,則不需要新增屬性。如要變更方向,請加入屬性和下列任一值之一:

  • row:項目以一列排列。
  • row-reverse: 項目會在彈性容器結尾以一列方式排列。
  • column:項目以欄形式排列。
  • column-reverse:項目會以直欄排列方式從 Flex 容器的結尾開始排列。

您可以使用下方示範中的項目群組,試用所有值。

改變項目流程和無障礙功能

使用任何屬性重新排序視覺顯示時,請務必小心謹慎,不得依照 HTML 文件中的順序排列,否則可能會對無障礙體驗造成負面影響。row-reversecolumn-reverse 值就是這方面的好例子。重新排序只會影響視覺順序,而非邏輯順序。請務必瞭解,因為邏輯順序是螢幕閱讀器朗讀內容的順序,且所有使用鍵盤瀏覽的使用者都會遵循。

在接下來的影片裡,您可以看到在反向列版面配置中,連結間的 Tab 會如何解除連結,因為鍵盤導覽跟 DOM 而非視覺顯示。

任何可變更彈性容器或格狀容器中項目順序的操作都可能導致這個問題。因此,任何重新排序作業都應進行徹底測試,確保不會讓部分使用者難以使用您的網站。

如需詳細資訊,請參閱:

書寫模式與方向

根據預設,Flex 項目會是一列。列會沿著句子在書寫模式和劇本方向流動。也就是說,如果您使用阿拉伯文 (由右至左的文字方向),項目就會排列在右側。分頁順序也會從右側開始,因為阿拉伯文句子就是從右側開始閱讀。

如果您使用的是垂直書寫模式 (例如某些日文字型),則一列會從上到下垂直顯示。請嘗試變更這個示範中使用垂直書寫模式的 flex-direction

因此,根據預設,Flex 項目的運作方式會連結至文件的寫入模式。大多數的教學課程都是以英文或其他由左至右的橫向書寫模式編寫。這樣一來,您就能輕鬆假設 Flex 項目會排在左側,並水平執行。

由於主要軸和跨軸加上書寫模式需要考量,Flexbox 中介紹的「start」和「end」而非在上方、底部、左側和右側,可能會更容易理解。每個軸都有起點和終點。主軸的起點稱為 main-start。所以,Flex 系列的初始順序為從主要開始排列。該軸的結尾是 main-end。交叉軸的起點為 cross-start,終點為 cross-end

上述字詞的有標籤圖表

包裝 Flex 項目

flex-wrap 屬性的初始值為 nowrap。也就是說,如果容器的空間不足,項目就會溢出。

一個內含九個項目的 Flex 容器,項目已縮小,因此一行只顯示一個字詞,但沒有足夠的空間可並排顯示,因此 Flex 項目已延伸到容器的方塊外。
一旦達到最小內容大小,Flex 項目就會開始溢出容器

使用初始值顯示的項目會盡可能縮小,直到發生溢位為止,直到達到 min-content 大小為止。

如要讓項目進行包裝,請將 flex-wrap: wrap 新增至 Flex 容器。

.container {
  display: flex;
  flex-wrap: wrap;
}

當彈性容器包裝時,會建立多個彈性線。在空間分佈方面,每一行都以新的彈性容器的形式運作。因此,如果您要將資料列換行,就無法讓第 2 列的內容與第 1 列上方的內容對齊。這就是 Flexbox 代表單一維度的意義。 您可以控制單一軸 (一列或一欄) 的對齊方式,但無法同時控制兩者,因為我們可以在格線中執行這項操作。

flex-flow 速記法

您可以使用 flex-flow 速記符號設定 flex-directionflex-wrap 屬性。舉例來說,如要將 flex-direction 設為 column 並允許項目換行:

.container {
  display: flex;
  flex-flow: column wrap;
}

控制 Flex 項目內的空白

假設容器的空間比顯示項目所需的空間還多,項目會在開頭排成一列,且不會擴大填滿空間。並在內容大小達到上限時停止成長。這是因為 flex- 屬性的初始值為:

  • flex-grow: 0:項目不會增加。
  • flex-shrink: 1:項目可縮小至小於其 flex-basis
  • flex-basis: auto:項目的基本大小為 auto

這可以由關鍵字值 flex: initial 表示。flex 簡寫屬性或 flex-growflex-shrinkflex-basis 的長邊會套用至彈性容器的子項。

如要讓項目變大,並讓大型項目比小型項目有更多空間,請使用 flex:auto。您可以使用上述示範試試看。這會將屬性設為:

  • flex-grow: 1:項目可以大於其 flex-basis
  • flex-shrink: 1:項目可縮小至小於 flex-basis
  • flex-basis: auto:項目的基本大小為 auto

使用 flex: auto 會導致項目最終大小不同,因為項目之間共用的空間會在每個項目以最大內容大小排版共用。因此大型項目會增加較多空間。強制所有項目採用一致的大小,並忽略內容大小從 flex:auto 變更為 flex: 1 的情況。

這會解壓縮為:

  • flex-grow: 1:項目可以大於其 flex-basis
  • flex-shrink: 1:項目可以縮小至小於其 flex-basis 的項目。
  • flex-basis: 0:項目的基本大小為 0

使用 flex: 1 表示所有項目的大小為零,因此 Flex 容器中的所有空間都可供分配。由於所有項目的 flex-grow 因子皆為 1,而這些項目會均等增長,而空間的共用方式也相同。

允許項目以不同速度成長

您不需要將所有項目設為 1flex-grow 因子。您可以為 Flex 項目提供不同的 flex-grow 因數。在下方示範中,第一個項目有 flex: 1,第二個 flex: 2,第三個 flex: 3。這些項目從 0 變大,因此彈性容器中的可用空間會共用為六個。第一項項目獲得一半,第二項項目獲得兩半,第三項項目獲得三半。

您可以從 autoflex-basis 執行相同操作,但需要指定三個值。第一個值是 flex-grow、第二個 flex-shrink 和第三個 flex-basis

.item1 {
  flex: 1 1 auto;
}

.item2 {
  flex: 2 1 auto;
}

這是較不常見的用途,因為使用 autoflex-basis 是為了讓瀏覽器判斷空間分配。不過,如果您想讓項目的成長幅度略高於演算法判斷的值,這項功能可能就很實用。

重新排序 Flex 項目

您可以使用 order 屬性重新排序 Flex 容器中的項目。這個屬性可讓您在序數群組中排序項目。項目會按照 flex-direction 指定的方向排列,先顯示最小值。如有多個項目使用相同的值,系統會與其他含有該值的項目一併顯示。

以下範例說明此排序方式。

隨堂測驗

測驗您對 Flexbox 的瞭解

預設的 flex-direction

row
根據預設,Flexbox 會將項目排入一列,並在開始時排列這些項目。開啟包裝後,它會繼續建立子項流入的資料列。
column
將 flex-direction 設為 column 是堆疊元素的好方法,但並非預設值。

根據預設,Flex 容器會納入子項。

true
必須啟用包裝功能。
false
使用 flex-wrap: wrap 搭配 display: flex 包裝子項

一個 Flex 子項的項目似乎遭到擠壓,哪一個 Flex 屬性有助於緩解這個問題?

flex-grow
這項屬性說明元素是否可超出基礎大小,而非在基礎下應如何運作。
flex-shrink
是,此屬性說明在寬度低於基礎時如何處理大小調整。
flex-basis
這可以提供調整大小的起點,但不包括在寬度低於基準點的情況下 (如在稀疏的情況下),如何處理大小調整。

Flexbox 對齊方式總覽

Flexbox 帶來一組屬性,可用於對齊項目和分配項目間的空間。這些屬性在加入自己的規格後變得很實用,您也會在格狀版面配置中看到這些屬性。您可以從這裡瞭解使用 Flexbox 時的運作方式。

這組房源可放入兩個群組。空間分佈屬性和對齊屬性。 分配空間的屬性如下:

  • justify-content:主軸的空間分佈情形。
  • align-content:跨軸的空間分佈情形。
  • place-content:用於設定上述兩項屬性的簡寫。

用於 Flexbox 對齊的屬性:

  • align-self:在交叉軸上對齊單一項目。
  • align-items:對齊交叉軸上的所有項目。

如果您使用的是主軸,則屬性會以 justify- 開頭。在交叉軸上,這些值的開頭為 align-

在主軸上分配空間

使用先前提到的 HTML 時,Flex 項目會以一列方式排版,主軸上會有空白空間。項目不夠大,無法完全填滿 Flex 容器。由於 justify-content 的初始值為 flex-start,因此項目會排列在 Flex 容器的開頭。項目會在開頭排列,任何額外的空格則會放在結尾。

justify-content 屬性新增至 Flex 容器,並將其值設為 flex-end,項目就會排列在容器的結尾,空白空間則會放在開頭。

.container {
  display: flex;
  justify-content: flex-end;
}

您也可以使用 justify-content: space-between 在項目之間分配間距。

請試試示範中的部分值,並參閱 MDN 瞭解完整的可能值集。

前往 flex-direction: column

如果您已將 flex-direction 變更為 column,則 justify-content 就會在資料欄上運作。如要在以資料欄形式運作時讓容器有額外空間,您必須為容器提供 heightblock-size。否則您沒有可以散佈訊息的空間。

請嘗試使用不同的值,這次使用彈性容器的欄版面配置。

在彈性線之間分配空間

使用包裝的 Flex 容器時,您可能會在交錯軸上有可用的空間。在這種情況下,您可以使用 align-content 屬性,並設定與 justify-content 相同的值。justify-content 會根據預設將項目對齊至 flex-start,但 align-content 的初始值為 stretch。將屬性 align-content 新增至 Flex 容器,即可變更該預設行為。

.container {
  align-content: center;
}

請在示範中試試這個功能。這個範例包含折行的 Flex 項目,容器則包含 block-size,以便我們保留一些空白空間。

place-content 簡寫

如要同時設定 justify-contentalign-content,您可以使用 place-content 搭配一或兩個值。如果您同時指定第一個值用於 align-content,第二個值用於 justify-content,系統會為兩個軸使用單一值。

.container {
  place-content: space-between;
  /* sets both to space-between */
}

.container {
  place-content: center flex-end;
  /* wrapped lines on the cross axis are centered,
  on the main axis items are aligned to the end of the flex container */
}

對齊交叉軸上的項目

在交錯軸上,您也可以使用 align-itemsalign-self 將項目對齊在 Flex 線上。這項對齊功能可用的空間取決於 Flex 容器的高度,或在項目套用包裝時的 Flex 行。

align-self 的初始值為 stretch,因此一列中的 Flex 項目會預設為延伸至最高項目的高度。如要變更這個設定,請將 align-self 屬性新增至任何 Flex 項目。

.container {
  display: flex;
}

.item1 {
  align-self: flex-start;
}

請使用下列任一值讓項目對齊:

  • flex-start
  • flex-end
  • center
  • stretch
  • baseline

請參閱 MDN 的完整值清單

接下來的示範包含單行含有 flex-direction: row 的 Flex 項目。最後一個項目會定義 Flex 容器的高度。第一個項目具有 align-self 屬性,值為 flex-start。請嘗試變更該屬性的值,查看該屬性值在交叉軸上的移動方式。

align-self 屬性會套用至個別項目。align-items 屬性可套用至彈性容器,將所有個別 align-self 屬性都設為群組。

.container {
  display: flex;
  align-items: flex-start;
}

在下一項示範中,請嘗試變更 align-items 的值,讓交叉軸上的所有項目保持一致。

為什麼彈性容器中沒有 justify-self?

Flex 項目會在主軸上當做一組群組。因此,您無法將個別項目從該群組中分割出來。

在格線版面配置中,justify-selfjustify-items 屬性會在內嵌軸上運作,以便在格線區域內對齊該軸上的項目。由於 Flexbox 會將項目視為群組,因此這些屬性不會在 Flex 上下文中實作。

值得一提的是,彈性容器確實能與自動邊距搭配使用。如果您需要將某個項目從群組中分割,或是將群組分割成兩個群組,可以套用邊界來執行這項操作。在下方範例中,最後一個項目的左邊空白為 auto。自動邊界會吸收套用方向的所有空間。也就是說,它會將項目推送至右側,進而分割群組。

如何垂直和水平置中項目

對齊屬性可用於將項目置中,放置在另一個方塊中。justify-content 屬性會將項目對齊主軸 (即列)。交錯軸上的 align-items 屬性。

.container {
  width: 400px;
  height: 300px;
  display: flex;
  justify-content: center;
  align-items: center;
}

進行隨堂測驗

測試您的 Flexbox 知識

.container {
  display: flex;
  direction: ltr;
}

如要與 Flexbox 垂直對齊,請使用

校正關鍵字
很好
說明關鍵字
抱歉
.container {
  display: flex;
  direction: ltr;
}

如要與 Flexbox 水平對齊,請使用

校正關鍵字
抱歉
將關鍵字合理化
很好
.container {
  display: flex;
  direction: ltr;
}

根據預設,Flex 項目會對齊 stretch。如果您想為子項使用內容大小,會使用下列哪種樣式?

justify-content: flex-start
左右屬性為水平對齊,而非垂直對齊。
align-content: start
content 會對齊 Flex 線條,而非對齊子項項目。
height: auto
這麼做不會有任何影響。
align-items: flex-start
是,我們想讓它們垂直對齊「頂端」或「開始」,這樣就會移除預設的延展值,改用內容高度。

資源