1227 字
6 分鐘

Flex overflow: auto 不捲動?從巢狀祖先檢查 min-height: 0

側邊工具或聊天室做成直向 Flex,標頭固定、下方內容設 overflow: auto,卻變成整頁往下長。先檢查外層是否真的限制高度,再沿著 Flex 主軸找出不能縮小的 item;巢狀結構裡,常要調整的是捲動區的祖先。

不能只在任何一層貼上 min-height: 0 就宣布修好。你需要知道誰分配高度、誰允許縮小,以及誰負責捲動。

最裡面的 overflow: auto,管不到上層的最小高度#

Flexbox 規格的自動最小尺寸與主軸、overflow 條件有關。對非捲動的 Flex item,內容可能形成自動最小尺寸;捲動容器的自動最小尺寸則有不同處理。因此「每個 Flex 子元素都一定要 min-height: 0」並不準確。

本文刻意使用巢狀結構:

shell:直向 Flex,高度 320px
├─ header:48px
└─ workspace:Flex item,也是一個直向 Flex 容器
└─ pane:Flex item,overflow: auto
└─ content:900px 高的內容

pane 雖然負責捲動,但未限制的 workspace 仍可能按內容最小高度長大,讓下層拿到足以裝下內容的空間。內容根本沒有超過 pane 自己的高度,就不會出現你期待的局部捲動。

三組對照,保留壞掉的情境才看得出差別#

將下列完整 HTML 存成 flex-scroll-demo.html,在瀏覽器開啟。檔案沒有外部資源或腳本,提供三組相同內容:

<!doctype html>
<html lang="zh-Hant">
<meta charset="utf-8">
<title>巢狀 Flex 捲動對照</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font-family: sans-serif; }
.cases { display: flex; flex-wrap: wrap; gap: 24px; }
.shell { width: 280px; height: 320px; display: flex;
flex-direction: column; outline: 2px solid #555; }
header { flex: 0 0 48px; background: #cde9df; }
.workspace { flex: 1; display: flex; flex-direction: column; }
.pane { flex: 1; overflow: auto; }
.content { height: 900px; padding: 12px;
background: linear-gradient(#f7c4b6, #b4d6f3); }
.fixed .workspace { min-height: 0; }
.unbounded { height: auto; }
</style>
<div class="cases">
<section>
<h2>祖先未縮小</h2>
<div class="shell broken"><header>固定標頭</header>
<div class="workspace"><div class="pane" tabindex="0"
role="region" aria-label="原始內容區">
<div class="content">內容開頭<br>向下捲動檢查</div>
</div></div>
</div>
</section>
<section>
<h2>祖先 min-height: 0</h2>
<div class="shell fixed"><header>固定標頭</header>
<div class="workspace"><div class="pane" tabindex="0"
role="region" aria-label="修正內容區">
<div class="content">內容開頭<br>向下捲動檢查</div>
</div></div>
</div>
</section>
<section>
<h2>外層沒有高度上限</h2>
<div class="shell fixed unbounded"><header>固定標頭</header>
<div class="workspace"><div class="pane" tabindex="0"
role="region" aria-label="無上限內容區">
<div class="content">內容開頭<br>向下捲動檢查</div>
</div></div>
</div>
</section>
</div>
</html>

第一組不加修正;第二組只在 workspace 加 min-height: 0;第三組再把 shell 改成自動高度。這樣能分辨「祖先不肯縮」與「根本沒有高度限制」,避免兩個問題用同一個補丁處理。

正式版面不必照抄 320px。你可以從 viewport 或上層容器分配高度,但必須確認最終有可計算的限制。height: 100% 如果沒有對應的包含塊高度,也不會憑空建立預算。

量測真正的 pane,不用整頁捲軸判定#

在 DevTools Console 執行下列唯讀量測。先開啟範例,不是在任意網站貼上:

console.table([...document.querySelectorAll('.shell')].map(shell => {
const workspace = shell.querySelector('.workspace');
const pane = shell.querySelector('.pane');
return {
case: shell.className,
shell: shell.clientHeight,
workspace: workspace.clientHeight,
pane: pane.clientHeight,
content: pane.scrollHeight,
minHeight: getComputedStyle(workspace).minHeight,
};
}));

第二組應讓 workspace 與 pane 使用標頭之外的 272px,而 pane.scrollHeight 仍是 900px。第一組可觀察中間祖先被內容撐高;第三組即使 min-height: 0 生效,外層仍會跟著內容長大。

接著把焦點移到第二組內容區,實際向下捲動,確認 pane.scrollTop 改變、標頭保持原位。範例保留第一與第三組的溢位,因此整張測試頁出現垂直捲軸是預期現象,不能拿它判定第二組修正失敗。

本機 Chrome 量測結果為:第一組 pane 900px、第二組 272px、第三組 900px;第二組以 End 鍵捲至 scrollTop 628,內容高度仍為 900px。本文的本機驗收只涵蓋這個對照頁,沒有測試其他瀏覽器、螢幕閱讀器、行動裝置鍵盤或正式專案。範例加上可聚焦的具名 region,方便單獨操作內容區;正式介面仍要檢查鍵盤焦點順序與內容可達性。

還是捲不動,再沿高度鏈往外找#

若第二組模式放回專案仍失敗,逐層檢查從 pane 到有高度限制容器之間的 Flex item。某一層有固定 min-height、內容尺寸限制,或另一個不肯縮小的祖先,都可能改變可用空間。只在最裡面再加一次零,無法解除外層的限制。

overflow: hidden 會改變溢位處理,可能讓表面尺寸變小,也可能裁掉內容;若需求是可讀取全部訊息,請以內容能否捲到尾端驗收。橫向長文字撐開欄位則看 Grid 的 minmax 與 min-width 邊界;sticky 行為受哪層捲動容器影響,可接著看 單軸 overflow。

參考資料:

CSS Flexbox:Automatic Minimum Size of Flex Items

MDN:min-height

MDN:Overflowing content

Flex overflow: auto 不捲動?從巢狀祖先檢查 min-height: 0
https://laplusda.com/posts/css-flex-min-height-zero-scroll/
作者
Zero
發佈於
2026-10-31
許可協議
CC BY-NC-SA 4.0