【佈局技巧】Flex 佈局下居中溢位捲動截斷問題

2023-12-07 12:05:48

在頁面佈局中,我們經常會遇到/使用這麼一類常見的佈局,也就是列表內容水平居中於容器中,像是這樣:

<ul class="g-contaner">
    <li></li>
    <li></li>
</ul>
ul {
    width: 500px;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    justify-content: center;
    align-items: center;
    gap: 10px;
}

效果如下:

這裡,外層的容器是定寬的,內層的 flex-item 也是定寬的。

當 flex-item 個數較小時,是沒有問題的。但是,如果當元素內容過多,並且設定了 flex-wrap: nowrap 的話,內容就會溢位容器:

此時,我們有幾種解法,其中一種便是給父容器設定 overflow: auto 或者 overflow: hidden,讓父容器可以捲動,像是這樣:

ul {
    // ...
    overflow: auto;
}

效果就變成了這樣:

我們嘗試捲動一下這個容器,會發現一個致命問題:容器只能向左捲動,無法向右捲動,因此只能看到後半部分被截斷的內容,而無法看到前半部分被截斷的內容

什麼意思呢?結合上面的 Gif 與下面這張示意圖,一看就懂:

針對這個問題。其中一類比好好的解法在於,當 flex-item 不足以溢位時候,flex-item 居中展示,而當 flex-item 的數量溢位父容器寬度時候,佈局上採用類似於 justify-content: flex-start 的樣式進行排布,這樣可以保證內容在捲動的過程中能夠全部看到

正常效果應該如下:

上面第一、第二行就是 flex-item 不足以溢位時候,flex-item 居中展示, 而第三行 ,就是當 flex-item 的數量溢位父容器寬度時候,佈局上採用類似於 justify-content: flex-start 的樣式進行排布,這樣可以保證內容在捲動的過程中能夠全部看到。

因此,本文我們將一起探討一下,在面對這個問題時的幾種不同方式的解法。

方法一:Flex 佈局下關鍵字 safe、unsafe

其實,規範也已經注意到了佈局下的這個居中捲動問題。

因此,在從 Chrome 115 開始,flex 佈局下新增了兩個關鍵字 safeunsafe

基於 CSS Box Alignment Module Level 3,明確列出了這種安全(safe)不安全(unsafe) 的佈局說明:

而今天,我們可以直接在對齊模式中,通過 safe 關鍵字解決這個問題。

我們簡單改造一下上述我們的 flex 佈局程式碼,將 justify-content: center 改為 justify-content: safe center 即可:

ul {
    width: 500px;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
 -  justify-content: center;
 +  justify-content: safe center;
    align-items: center;
    gap: 10px;
}

此時,flex 佈局就能自動識別當前 flex 容器下的 flex-item 數量是否超出容器寬度/高度,從而改變對齊方式

完整的程式碼,你可以戳這裡自己感受:CodePen Demo -- 使用 Safe 關鍵字解決 Flex 居中溢位問題

目前而言,這個方法唯一的問題在於 相容性safe 關鍵字的大範圍使用,還需要靜待一段時間。

方法二:使用 margin: auto 替代 justify-content: center

因此,我們有必要繼續去探尋其它解決方案。

在之前,有發過另外兩篇 flex 相關技巧性的文章 --

  1. 探祕 flex 上下文中神奇的自動 margin - 掘金
  2. 水平垂直居中深入挖掘 - 掘金

除去 justify-content: center 之外,其實我們還可以利用 margin: auto 實現子 flex-item 的水平居中。

我們改造一下文章一開始的示意 DEMO:

ul {
    width: 500px;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    gap: 10px;
}
li {
    margin: auto;
}

當,flex-item 數量不足以溢位容器寬度時,效果如下:

此時,flex-item 在 margin: auto 的作用下,會均分整個容器的剩餘空間,並且是水平和垂直方向上的。

用規範的話說就是,設定了 margin: auto 的元素,在通過 justify-contentalign-self 進行對齊之前,任何正處於空閒的空間都會分配到該方向的自動 margin 中去。

所以,margin: auto 也是一種居中非常重要的技巧,雖然我們常將這個技巧用於 flex 佈局下的垂直居中。可以翻看一下上面提供的兩篇文章。

有趣的是,當 flex-item 的數量溢位父容器寬度時候,由於沒有剩餘空間了,此時 margin: auto 其實相當於失效了,因此佈局上的效果同樣也是採用類似於 justify-content: flex-start 的效果進行排布。

同樣能達到我們的目的:

完整的程式碼,你可以戳這裡自己感受:CodePen Demo -- 使用 margin:auto 解決 Flex 居中溢位問題

方法三:額外巢狀一層

上面的 margin:auto 雖然沒有相容性問題,但是有一點點瑕疵。我們仔細對比 margin: autojustify-content: center 在 flex-item 不足以溢位下的表現:

瑕疵在於,使用 margin: auto 的方式,flex-item 之間的間距是不可控。因為它們始終會去平分剩餘空餘空間。

2023-12-06 更新,基於 margin: auto 不可控的方式,其實通過只給子 item 的首個元素新增 margin-left: auto,給尾元素新增 margin-right: auto,其實是可以解決間距問題的。

所以,相容性最好的方式,就是我們多疊加一層,同樣可以巧妙的解決這個問題。

原結構:

<ul class="g-contaner">
    <li></li>
    // ...
    <li></li>
</ul>

改造後的結構:

<ul class="g-contaner">
    <ul class="g-wrap">
        <li></li>
        // ...
        <li></li>
    </ul>
</ul>

改造後的 CSS:

.g-contaner {
    width: 500px;
    height: 200px;
    display: flex;
    flex-wrap: nowrap;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    overflow: auto;
}

.g-wrap {
    display: flex;
    gap: 10px;
    max-width: 100%;
}

我們通過多設定了一層 g-wrap,並且設定了 max-width: 100%,當然,它也是一個 flex 容器。

因此當:

  1. .g-wrap 內 flex item 寬度不足 100% 時,整個 .g-wrap 受到其父容器的 justify-content: center 限制會表示為水平居中;
  2. .g-wrap 內 flex item 寬度超出 100% 時,由於設定了 max-width: 100%,所以,整個容器最大寬度就是 .g-container 的寬度。此時的子 flex item 的表現就是預設的 justify-content: flex-start,因此內容也是從頭開始展示,捲動場景下沒有問題

至此,我們藉助多巢狀一層,同樣完美的解決了整個問題。其效果與方法一類似,就不再額外貼 Gif 圖。

完整的程式碼,你可以戳這裡:CodePen Demo - 使用額外巢狀層解決 Flex 居中溢位問題

總結一下

好,我們快速總結一下三種方式的優劣對比:

  1. 方法一:Flex 佈局下關鍵字 safe、unsafe,修改程式碼量最少,效果完美,核心問題在於相容性目前不佳;
  2. 方法二:使用 margin: auto 替代 justify-content: center,相容性好,問題在於 flex item 不足父容器 100% 時,元素之間間距無法控制;
  3. 方法三:額外巢狀一層,效果完美,改造量略多一點點。

三種方式各有優劣,基於實際面臨的業務場景再做選擇。

同時,本文舉例採用了水平方向的例子,實際在業務中,我們同樣可能會遇到垂直方向一樣的問題,本文中的解法都是通用的。並且,基於 safe 的解法中,除了 justify-content: safe center 外,safe 關鍵字還可以應用於 align-itemsalign-self,實際使用時,結合規範,選取最適合的寫法。

最後

好了,本文到此結束,希望本文對你有所幫助