侧边进度条必须用 position: fixed 实现,不可用 sticky;应挂载于 body 下,设 top/left/right、height: 100vh、z-index: 1000 和 pointer-events: none;滚动监听须用 document.documentElement.scrollTop 并做范围约束;宽度控制优先使用 CSS 自定义属性配合 transition。

侧边进度条必须用 position: fixed,不能用 sticky
滚动时保持在视口左侧/右侧不动,position: fixed 是唯一可靠方案。position: sticky 在多数滚动场景下会失效——比如父容器有 transform、overflow 或 flex 布局干扰时,它会直接退化为 static。固定定位绕过这些限制,只依赖视口坐标。
常见错误是把进度条塞进某个 .main-content 内部再设 sticky,结果一滚动就消失。正确做法是让进度条脱离文档流,直接挂载在 <body> 下或顶层容器中。
-
top: 0+left: 0(或right: 0)定位起点 - 显式设置
height: 100vh,避免因内容高度不足导致条变短 - 加
z-index: 1000防止被其他元素遮挡 - 用
pointer-events: none避免遮挡下方可点击区域(如侧边导航)
滚动监听要读 documentElement.scrollTop,不是 body.scrollTop
不同浏览器对 document.body.scrollTop 的支持不一致,尤其在 Chrome 80+ 和 Safari 中,它常返回 0,真实滚动值藏在 document.documentElement.scrollTop 里。只读一个会导致进度条卡在 0%。
安全写法是取两者的最大值:Math.max(document.body.scrollTop, document.documentElement.scrollTop)。但更稳妥的是统一用 document.documentElement.scrollTop,并确保页面没有触发怪异的盒模型模式(比如未声明 <!DOCTYPE html>)。
立即学习“前端免费学习笔记(深入)”;
- 别用
window.scrollY—— 它在部分 iOS Safari 版本中存在延迟更新问题 - 监听函数里别直接改
style.width,优先用 CSS 变量(见下一条) - 加
throttle(例如 60ms 间隔),否则高频 scroll 事件会拖慢渲染
用 CSS 自定义属性控制宽度,别直接操作 style.width
直接写 el.style.width = x + '%' 会覆盖所有 CSS 过渡和伪元素效果,且无法配合 linear-gradient 实现多段色块。应该把进度值抽成变量,让 CSS 自己算宽度。
示例结构:
.sidebar-progress {
--progress: 0;
width: 4px;
background: linear-gradient(to bottom, #4CAF50, #2196F3);
}
.sidebar-progress::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: calc(var(--progress) * 1%);
background: inherit;
}JS 只需一句:el.style.setProperty('--progress', scrolledPercent)。CSS 中用 transition: --progress 0.3s ease-out(注意:现代浏览器已支持对自定义属性做过渡)。
- 如果兼容性要求覆盖 Safari 15.6 以下,降级用
width: calc(var(--progress) * 1%)+transition: width - 务必给进度条元素加
max-width: none,否则 Flex 容器里的flex-basis会截断宽度计算 - 用开发者工具的 “Computed” 面板确认
width是否随变量实时变化,这是调试关键
移动端需要处理 overscroll 和 touch 滚动偏差
iOS 和部分安卓 WebView 中,页面滚动到底部后继续拖拽会产生“回弹”,scrollTop 会短暂变成负数或超过 scrollHeight,导致进度条反向收缩或跳到 100% 后又退回。这不是 bug,是系统行为。
解决方法是在计算时 clamp 范围:
const scrollTop = Math.max(0, Math.min( document.documentElement.scrollTop, document.documentElement.scrollHeight - window.innerHeight ));
另外,纯 CSS fixed 在 iOS 上可能因 viewport 设置异常而偏移,建议在 <head> 中明确声明:
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
- 别依赖
touchstart/touchmove手动模拟滚动——这会破坏原生惯性滚动体验 - 侧边条宽度小于 6px 时,在 iPhone 上容易误触,建议最小设为
4px并加pointer-events: none - 如果页面用了
scroll-behavior: smooth,监听scroll事件仍能正常工作,无需额外处理
滚动进度本身不难,难的是各种边界情况叠加:iOS 回弹、Flex 干扰、变量过渡兼容性、viewport 错位……每个点单独看都小,合起来就容易漏掉一两个,导致进度条在某台设备上完全不动。


















