可用纯CSS实现Bootstrap 5横向滚动:设overflow-x:auto、scrollbar-gutter:stable both-edges、-webkit-overflow-scrolling:touch;父容器d-flex+flex-nowrap+gap,子项flex-shrink-0+固定宽。

用 overflow-x + scrollbar-gutter 实现纯 CSS 横向滚动
Bootstrap 5 默认不提供无 JS 的横向滚动组件,但完全可以用原生 CSS 实现——关键不是靠 Bootstrap 类,而是控制容器的溢出行为和滚动条占位。
常见错误是直接给 .list-group 或 .d-flex 加 overflow-x: auto,结果在 Safari 或某些安卓 WebView 中滚动卡顿、无法拖拽,或滚动条遮挡内容。
- 必须给容器设固定宽度(如
width: 100%)且white-space: nowrap防止子项换行 - 子项用
display: inline-block或flex-shrink: 0(配合display: flex容器),否则会被压缩 - 加
scrollbar-gutter: stable both-edges避免滚动条出现时内容跳动(Chrome 112+ / Firefox 119+ 支持;老版本可退化为padding-right: 16px)
为什么 .d-flex + overflow-x 常常失效
因为 Bootstrap 5 的 .d-flex 默认是 flex-wrap: wrap,而横向滚动要求子项“不折行、不收缩”。一旦父容器宽度不够,Flex 项会自动换行或缩小,滚动就没了。
典型现象:在小屏上看到一堆元素堆成一列,而不是横向滚动。
立即学习“Java免费学习笔记(深入)”;
- 必须显式覆盖:
flex-wrap: nowrap和flex-shrink: 0(对每个子项) - 如果用
.col或.me-2等间距类,它们生成的是margin,会在末尾多出空白,导致滚动条永远拉不到底 —— 改用gap(需父容器display: flex)更可控 - 避免在滚动容器上用
.container或.row,它们自带 max-width 和 padding,干扰宽度计算
兼容性补救:Safari 和旧版 Chrome 的滚动惯性问题
Safari 默认关闭滚动惯性(-webkit-overflow-scrolling: touch 已废弃),导致手指松开后立刻停住,体验像“卡住”。这不是 Bug,是渲染策略差异。
错误做法:试图用 JS 监听 touchmove 模拟惯性 —— 这就违背了“不需要 JavaScript”的前提。
- 加
-webkit-overflow-scrolling: touch仍有效(尽管已废弃),多数 Safari 版本仍识别 - 确保容器有
will-change: scroll-position或transform: translateZ(0),触发硬件加速(尤其 iOS 15 以下) - 不要给滚动容器设
height,只设max-height,否则 Safari 可能截断内容
一个可用的最小代码块
下面这段可以直接复制进 Bootstrap 5 页面,不依赖任何 JS:
<div class="overflow-x-auto py-2" style="scrollbar-gutter: stable both-edges; -webkit-overflow-scrolling: touch;">
<div class="d-flex flex-nowrap gap-3 px-2">
<div class="bg-light border rounded p-3 flex-shrink-0" style="width: 180px;">卡片 1</div>
<div class="bg-light border rounded p-3 flex-shrink-0" style="width: 180px;">卡片 2</div>
<div class="bg-light border rounded p-3 flex-shrink-0" style="width: 180px;">卡片 3</div>
</div>
</div>
注意 flex-shrink-0 必须加在每个子项上,gap-3 替代 me-3,px-2 提供左右内边距防止首尾被切。
最易忽略的一点:横向滚动列表如果嵌套在 .modal-body 或 .tab-pane 这类“隐藏时 display: none”的容器里,首次显示时滚动宽度可能计算错误 —— 此时得靠 visibility: hidden + height: 0 替代,或者手动触发一次 resize 事件(但这就引入 JS 了)。纯 CSS 场景下,尽量别把它放在这类条件渲染区域里。


















