掌握彈性專案的換行
Flexbox 最初被設計為單維佈局工具——它處理將專案按行或列進行佈局——但不能同時處理兩者。然而,將 flex 專案換行是可能的,如果 flex-direction 是 row,則建立新行;如果 flex-direction 是 column,則建立新列。本指南解釋了 flexbox 的換行、其設計用途,以及哪些情況需要使用 CSS 網格佈局而不是 flexbox。
實現換行
flex-wrap 屬性的初始值是 nowrap。這意味著如果一組 flex 專案對於其 flex 容器來說太寬,它們將會溢位。要讓它們在過寬時換行,可以新增值為 wrap 的 flex-wrap 屬性,或者使用簡寫屬性 flex-flow 並設定值為 row wrap 或 column wrap。這樣,當專案溢位其容器時,它們就會換到新的一行或一列。
在此示例中,有十個 flex 專案,它們的 flex-basis 為 160px,並且可以放大和縮小。一旦一行中沒有足夠的空間再放置一個 160 畫素的專案,就會建立一條新的 flex 行。根據需要建立新行,直到所有專案都被放置好。由於專案可以放大,它們會伸展以完全填滿每一行。如果最後一行只有一個專案,它將伸展以填滿整行。
<div class="box">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
</div>
.box {
width: 500px;
border: 2px dotted rgb(96 139 168);
display: flex;
flex-wrap: wrap;
}
.box > * {
border: 2px solid rgb(96 139 168);
border-radius: 5px;
background-color: rgb(96 139 168 / 0.2);
flex: 1 1 160px;
}
flex 列的情況也是如此。要換行並建立新列,容器需要有一個高度。在列的情況下,專案會垂直拉伸以完全填滿每一列。
<div class="box">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
</div>
.box {
border: 2px dotted rgb(96 139 168);
height: 300px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.box > * {
border: 2px solid rgb(96 139 168);
border-radius: 5px;
background-color: rgb(96 139 168 / 0.2);
flex: 1 1 80px;
}
換行與 flex-direction
當與 flex-direction 結合使用時,換行的行為符合預期。如果 flex-direction 設定為 row-reverse,那麼專案將從容器的末端邊緣開始,並以反向順序排列成行。
<div class="box">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
</div>
.box {
border: 2px dotted rgb(96 139 168);
display: flex;
flex-wrap: wrap;
flex-direction: row-reverse;
width: 500px;
}
.box > * {
border: 2px solid rgb(96 139 168);
border-radius: 5px;
background-color: rgb(96 139 168 / 0.2);
flex: 1 1 160px;
}
請注意,反向只發生在行內(主軸)方向上。我們從右邊開始,然後換到第二行,再次從右邊開始。我們不是在兩個方向上都進行反向,即不是從容器底部向上排列!
單維佈局解析
正如我們從上面的例子中看到的,如果我們的專案被允許放大和縮小,當最後一行或一列的專案較少時,這些專案會放大以填補可用空間。
flexbox 沒有提供任何功能來讓一行中的專案與上一行的專案對齊——每條 flex 行都像一個新的 flex 容器。它處理的是主軸上的空間分配。如果只有一個專案,並且該專案被允許放大,它將填滿整個主軸,就像你只有一個專案的 flex 容器一樣。如果你想要二維佈局,那麼你可能需要的是網格佈局。
這個例子演示了其中的區別,它使用 CSS 網格佈局來建立一個佈局,該佈局包含儘可能多的至少 160px 寬的列,並將多餘的空間分配給所有列。我們使用了與上面flexbox 換行示例相同的 HTML,但對其設定了 display: grid。我們沒有使用在 flexbox 之外無效的 flex 簡寫屬性,而是透過 grid-template-columns 直接在容器上設定專案的最小寬度和放大能力。使用 CSS 網格,最後一個專案會停留在其網格單元格中;當最後一行專案較少時,網格專案不會拉伸。
<div class="box">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
</div>
.box {
border: 2px dotted rgb(96 139 168);
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
width: 500px;
}
.box > * {
border: 2px solid rgb(96 139 168);
border-radius: 5px;
background-color: rgb(96 139 168 / 0.2);
}
這就是一維佈局和二維佈局的區別。在像 flexbox 這樣的一維佈局方法中,我們只控制行或列。在二維網格佈局中,我們同時控制兩者。如果你想逐行進行空間分配,請使用 Flexbox。如果不想,請使用 CSS 網格。
基於 flexbox 的網格系統是如何工作的?
基於 flexbox 的佈局可以被強制對齊成像網格系統一樣,但這並非 flexbox 的設計初衷。如果你給 flex 專案分配了百分比寬度——無論是使用 flex-basis 還是直接為專案新增寬度並讓 flex-basis 的值為 auto——你可以營造出二維佈局的錯覺。
在此示例中,flex-grow 和 flex-shrink 都被設定為 0,以使 flex 專案變為不可伸縮。其靈活性透過百分比來控制。
<div class="box">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
</div>
* {
box-sizing: border-box;
}
.box {
width: 500px;
border: 2px dotted rgb(96 139 168);
display: flex;
flex-wrap: wrap;
}
.box > * {
border: 2px solid rgb(96 139 168);
border-radius: 5px;
background-color: rgb(96 139 168 / 0.2);
flex: 0 0 33.3333%;
}
這種技術可以讓你在交叉軸上對齊 flex 專案。然而,當你發現自己正在以這種方式為 flex 專案新增寬度,或者新增空的 flex 專案來佔據空間時,這通常是一個很好的跡象,表明你可能需要為該元件切換到 CSS 網格佈局。
在專案之間建立間距
要在 flex 專案之間建立間隙或間距,可以直接在 flex 容器上使用 gap 屬性,在相鄰的 flex 專案之間建立固定的空間。gap 屬性是 row-gap 和 column-gap 的簡寫。這些屬性指定了網格、flex 和多列布局中行與列之間的間距大小。
gap 屬性並不是唯一可以在專案之間增加空間的屬性。外邊距(margin)、內邊距(padding)、justify-content 和 align-content 也可以增加間距的大小,從而影響間距的實際尺寸。
要了解 gap 屬性與 margin 在兩個軸上的區別,請嘗試在下面的樣式表中更改容器 .box 的 gap 值,併為 .box > * 規則新增一個 margin 值。點選“Reset”按鈕可以恢復到之前的值。
<div class="wrapper">
<div class="box">
<div>One</div>
<div>Two</div>
<div>Three</div>
<div>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
</div>
</div>
.wrapper {
border: 2px dotted rgb(96 139 168);
width: 500px;
}
.box {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.box > * {
flex: 1 1 160px;
border: 2px solid rgb(96 139 168);
border-radius: 5px;
background-color: rgb(96 139 168 / 0.2);
}
摺疊的專案
flexbox 規範詳細說明了如果一個 flex 專案透過設定 visibility: collapse 而被摺疊時應該發生什麼。請參閱 MDN 關於 visibility 屬性的文件。規範對該行為的描述如下:
“在 flex 專案上指定
visibility: collapse會使其成為一個*摺疊的 flex 專案*,產生類似於在表格行或表格列上使用visibility: collapse的效果:摺疊的 flex 專案會從渲染中完全移除,但會留下一個‘支柱’(strut),以保持 flex 行的交叉軸尺寸穩定。因此,如果一個 flex 容器只有一條 flex 行,動態地摺疊或展開專案可能會改變 flex 容器的主軸尺寸,但保證不會影響其交叉軸尺寸,也不會導致頁面其餘部分的佈局‘晃動’。然而,摺疊後會重新進行 flex 行的換行,因此具有多行的 flex 容器的交叉軸尺寸可能會也可能不會改變。” - 摺疊的專案
如果你想用 JavaScript 來控制 flex 專案以顯示和隱藏內容,這種行為會很有用。規範中的例子就演示了這樣一種模式。
在下面的即時示例中,不換行的 flex 容器包含一行三個 flex 專案,它們被設定為等尺寸伸縮。第三個專案有多行內容,使得容器變高。align-items 的預設值是 normal;對於 flex 專案,normal 的行為與 stretch 相同,所以所有專案預設都會拉伸,以填充容器的交叉軸高度。
建立交叉軸尺寸的專案被設定為 visibility: collapse,這會根據瀏覽器的不同,摺疊或隱藏該 flex 專案。無論哪種情況,flex 容器都會保留一個交叉軸尺寸的*支柱*,即使它不可見。這樣,如果該專案變為可見,單行 flex 容器的交叉軸尺寸將不會改變。如果你從 CSS 中移除 visibility: collapse 或將值更改為 visible,你會看到專案出現,主軸空間在未摺疊的專案之間重新分配,而交叉軸尺寸保持不變。
注意:下面的示例請使用 Firefox,因為其他主流瀏覽器會將 collapse 處理為 hidden。
<div class="box">
<div>One</div>
<div>Two</div>
<div class="collapse">Three <br />has <br />extra <br />text</div>
</div>
.box {
border: 2px dotted rgb(96 139 168);
display: flex;
width: 600px;
}
.box > * {
flex: 1 1 200px;
border: 2px solid rgb(96 139 168);
border-radius: 5px;
background-color: rgb(96 139 168 / 0.2);
}
.collapse {
visibility: collapse;
}
以上是一個單行、不換行的 flex 容器,其固定尺寸為 600px,因此無論專案是可見還是摺疊,寬度都相同。重要的是要理解,雖然容器保留了摺疊專案交叉軸尺寸的支柱,但主軸尺寸並未保留。多行 flex 容器在從渲染中移除摺疊專案後會重新進行換行。摺疊專案在主軸方向上留下的新空間可能會導致未摺疊的專案被放置在與未摺疊時不同的行上。因為每一行都像一個獨立的單行 flex 容器一樣進行佈局,其構成在摺疊後可能會改變,所以它的交叉軸尺寸也可能改變。
下面的例子展示了這種行為。第三個 flex 專案被摺疊,因此它在主軸上不佔用任何空間(行內尺寸為 0)。摺疊時,它的支柱位於第一行第四個專案之後,第一行的高度足以容納第三個專案本應有的三行文字。然後,如果你取消折疊該專案(例如,透過移除 collapse 類),第一行將不再有足夠的水平空間容納第五個專案,它會移動到第二行。這導致第二行變高以適應其新成員的兩行文字,而最後一個 flex 專案被推到了新的一行。由於第二行變高和新增了第三行,flex 容器比之前高了很多。
注意:下面的示例請使用 Firefox,因為其他主流瀏覽器會將 collapse 處理為 hidden。
<div class="box">
<div>One</div>
<div>Two is the width of this sentence.</div>
<div class="collapse">Three <br />is <br />five <br />lines <br />tall.</div>
<div>Four</div>
<div>Five<br />Five</div>
<div>Six</div>
<div>Seven</div>
<div>Eight</div>
<div>Nine</div>
<div>Ten</div>
<div>Eleven is longer</div>
</div>
.box {
border: 2px dotted rgb(96 139 168);
width: 500px;
display: flex;
flex-wrap: wrap;
}
.box > * {
padding: 10px;
border: 2px solid rgb(96 139 168);
border-radius: 5px;
background-color: rgb(96 139 168 / 0.2);
flex: 1 1 auto;
min-width: 50px;
}
.collapse {
visibility: collapse;
}
如果這給你的佈局帶來了問題,可能需要重新考慮結構,例如,將每一行放入一個單獨的 flex 容器中,這樣它們就不會換行了。
使用 visibility: hidden 和 display: none
在之前的即時示例中,嘗試用 visibility: hidden 或 display: none 替代 visibility: collapse。使用 visibility: hidden,專案會變得不可見,但其盒子仍在格式化結構中保留,因此它的行為仍然像是佈局的一部分。當你使用 display: none 時,該專案會從格式化結構中完全移除。它不僅不可見,其結構也被移除了。這意味著計數器會忽略它,像過渡(transition)之類的效果也不會執行。