在頁面佈局中,我們經常會遇到/使用這麼一類常見的佈局,也就是列表內容水平居中於容器中,像是這樣:
<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 佈局下新增了兩個關鍵字 safe
和 unsafe
。
基於 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 相關技巧性的文章 --
除去 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-content
和align-self
進行對齊之前,任何正處於空閒的空間都會分配到該方向的自動 margin 中去。
所以,margin: auto
也是一種居中非常重要的技巧,雖然我們常將這個技巧用於 flex 佈局下的垂直居中。可以翻看一下上面提供的兩篇文章。
有趣的是,當 flex-item 的數量溢位父容器寬度時候,由於沒有剩餘空間了,此時 margin: auto
其實相當於失效了,因此佈局上的效果同樣也是採用類似於 justify-content: flex-start
的效果進行排布。
同樣能達到我們的目的:
完整的程式碼,你可以戳這裡自己感受:CodePen Demo -- 使用 margin:auto 解決 Flex 居中溢位問題
方法三:額外巢狀一層
上面的 margin:auto
雖然沒有相容性問題,但是有一點點瑕疵。我們仔細對比 margin: auto
與 justify-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 容器。
因此當:
.g-wrap
內 flex item 寬度不足 100% 時,整個.g-wrap
受到其父容器的justify-content: center
限制會表示為水平居中;- 當
.g-wrap
內 flex item 寬度超出 100% 時,由於設定了max-width: 100%
,所以,整個容器最大寬度就是.g-container
的寬度。此時的子 flex item 的表現就是預設的justify-content: flex-start
,因此內容也是從頭開始展示,滾動場景下沒有問題
至此,我們藉助多巢狀一層,同樣完美的解決了整個問題。其效果與方法一類似,就不再額外貼 Gif 圖。
完整的程式碼,你可以戳這裡:CodePen Demo - 使用額外巢狀層解決 Flex 居中溢位問題
總結一下
好,我們快速總結一下三種方式的優劣對比:
- 方法一:Flex 佈局下關鍵字 safe、unsafe,修改程式碼量最少,效果完美,核心問題在於相容性目前不佳;
- 方法二:使用 margin: auto 替代 justify-content: center,相容性好,問題在於 flex item 不足父容器 100% 時,元素之間間距無法控制;
- 方法三:額外巢狀一層,效果完美,改造量略多一點點。
三種方式各有優劣,基於實際面臨的業務場景再做選擇。
同時,本文舉例採用了水平方向的例子,實際在業務中,我們同樣可能會遇到垂直方向一樣的問題,本文中的解法都是通用的。並且,基於 safe 的解法中,除了 justify-content: safe center
外,safe
關鍵字還可以應用於 align-items
和 align-self
,實際使用時,結合規範,選取最適合的寫法。
最後
好了,本文到此結束,希望本文對你有所幫助 😃
想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 😄
更多精彩 CSS 技術文章彙總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。
如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。