clientWidth 自动扣除滚动条宽度,适合填满内容区;offsetWidth 包含滚动条,用于获取总占位宽度;滚动条宽度需动态计算 offsetWidth - clientWidth,不可硬编码。

clientWidth 不能直接测滚动条宽度,但能帮你避开它
你没法用 CSS 单独“读取”滚动条宽度,getComputedStyle 查不到,width 也不包含它。真正能拿到“含滚动条的总占位宽度”的,只有 offsetWidth;而 clientWidth 返回的是“内容区 + padding,自动扣除滚动条所占像素”的值——它不告诉你滚动条多宽,但能让你安全地填满可用区域。
常见错误现象:
- 用
clientWidth设置子元素width,结果子元素触发二次滚动(因为没减掉滚动条) - 硬写
calc(100% - 17px),在 macOS overlay 滚动条、高 DPI 缩放、细滚动条模式下直接失效
正确做法:
- 需要“刚好填满容器内容区” → 用
clientWidth(它已自动减去滚动条) - 需要“知道整个元素占了多少物理像素” → 用
offsetWidth - 要算滚动条宽度 →
offsetWidth - clientWidth(仅当滚动条实际渲染时成立)
scrollbar-gutter: stable 能预防 layout shift,但不能替代 JS 读值
scrollbar-gutter: stable both 的作用是:即使内容没溢出,也在右侧预留滚动条位置,让 clientWidth 不会因滚动条出现/消失而突变。它不是测量工具,而是布局稳定器。
使用场景:
- 弹窗、抽屉、侧边栏等局部滚动容器,频繁切换内容时易抖动
- 需要动画平滑过渡、避免视觉跳变的交互组件
限制条件:
- 仅 Chromium 120+ 和 Safari 17.4+ 支持,Firefox 尚未实现
- 不改变
width计算逻辑,也不提供滚动条宽度数值 - 必须配合
overflow: auto或scroll才生效
box-sizing: border-box 是前提,否则所有尺寸都不可信
如果父容器或自身没设 box-sizing: border-box,那 clientWidth 和 offsetWidth 的差值还会混入 padding 和 border 的计算偏差,导致你误以为是滚动条问题,其实是盒模型模式不一致。
关键点:
- 全局重置必须包含伪元素:
* , *::before, *::after { box-sizing: border-box; } - 某些原生控件(如旧版 Safari 的
input[type="search"])需单独回退为content-box - border 必须写全三元组,比如
border: 1px solid #ccc,不能只写border: 1px
读 clientWidth / offsetWidth 必须等布局稳定
在 DOM 插入后立刻读,可能拿到旧值或 0。滚动条是否渲染、是否可见、是否被 UA 隐藏,都依赖最终布局结果。
可靠时机:
- 放在
requestAnimationFrame回调里(推荐) - 监听
resize事件后读(适合响应式容器) - 对局部滚动容器,用
scrollWidth > clientWidth判断是否真有溢出,再决定要不要读差值
IE 兼容兜底:读 document 宽度时用 document.documentElement.clientWidth || document.body.clientWidth,但局部容器必须用自身 ele.clientWidth。
滚动条宽度永远不是固定值,它随系统设置、缩放、设备类型浮动。唯一靠谱的做法,是每次需要时动态算 offsetWidth - clientWidth,而不是存一个“17px”到处用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











