滚动容器的尾部 padding 去哪了
给一个会横向滚动的 flex 容器加 padding: 0 20px,本意是首尾各留 20px。可在不少浏览器里,滚到最右时尾部那 20px 不见了——最后一项直接贴住容器右内边。根因是历史上浏览器计算可滚内容宽度(scrollWidth)时,只算到内容盒、把 padding-inline-end 排除在外,于是它落在滚不到的区域。这与居中即截断同属「flex + overflow 边界」,但那页是 justify-content: center 把首部推到负坐标,这页是尾部 padding 不计入可滚区。下面实测当前浏览器的真实表现。
1 · 滚到两端,实测首尾留白
容器设 padding: 14px 20px 且内容溢出。点按钮把它滚到最左 / 最右,用 getBoundingClientRect 量首项左缘、末项右缘到容器内边的可见距离:首部 20px 一般都在,尾部是否还在 20px 取决于本浏览器是否把 padding-inline-end 计入 scrollWidth。打开「末尾
spacer 修复」会在末尾插一个等宽占位,把那段留白塞进可滚内容里。
为什么是「尾部」而非「首部」。 滚动从 scrollLeft: 0 起算,首部 padding-inline-start 落在内容原点之前、天然在视野内;而 scrollLeft 的上限是
,一旦 scrollWidth 不含尾部 padding,最右就停在「最后一项右缘」,那 20px 永远滚不出来。规范(CSS Overflow)对此长期未统一,各引擎行为和版本都有差异——所以本页以实测为准。
2 · 三种绕法
| 做法 | 效果 | 代价 |
|---|---|---|
末尾占位 (::after / spacer) |
在内容末尾插一个 flex: none 的等宽占位,把尾部留白变成真实可滚内容。 |
多一个无语义节点;::after 在 flex 容器上可直接当 item 用,无需改 DOM。 |
最后一项 margin-inline-end |
给末项加右外边距,同样计入可滚宽度。 | 需选中「最后一项」(:last-child),且 margin 也属可滚内容。 |
border 代替 padding |
用透明 / 同色 border-inline 留白,border 不受该问题影响。 |
滚动条位置随 OS 不同(可能压在 border 上),且 border 不能用百分比。 |
原生正解正在路上。 gap decorations 所属的 CSS Gaps / Overflow 工作中,滚动容器的 padding 行为也在被重新明确;现代 Chromium 已倾向把 padding-inline-end 计入可滚区(本页 lab
会实测当前结果)。在普遍落地前,上面三种绕法仍是稳妥选择,其中末尾占位兼容性最好、对 OS 滚动条无副作用。
3 · 延伸
- Flexbox and padding · chenhuijing.com — 本页问题的出处:滚动 flex 容器尾部 padding 丢失的来龙去脉,以及 border / 伪元素两种绕法。
- Scrollable overflow & padding · csswg-drafts #129 — 滚动容器是否应把 padding-inline-end 计入 scrollable overflow 的长期讨论。