滚动阅读进度条需用document.documentElement.scrollTop和scrollHeight计算比例,通过requestAnimationFrame节流防重排;CSS用fixed定位、transition平滑动画;兼容移动端需添加passive:true的touchmove监听;避免闪动应延迟初始化或用ResizeObserver校准。

滚动时实时计算阅读进度
页面滚动过程中,需要持续获取当前可视区域顶部距离文档顶部的距离(scrollTop),再除以整个可滚动高度(document.documentElement.scrollHeight - window.innerHeight),就能得到 0–1 的阅读比例。注意必须用 document.documentElement 而非 document.body,否则在部分浏览器中会因盒模型差异导致计算偏移。
常见错误是直接监听 window.onscroll 并每次重算 DOM —— 这会频繁触发重排。应使用 requestAnimationFrame 节流:
let ticking = false;
function updateProgress() {
if (!ticking) {
requestAnimationFrame(() => {
const scrollTop = document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
const progress = Math.min(1, Math.max(0, scrollTop / scrollHeight));
document.getElementById('progress-bar').style.width = `${progress * 100}%`;
ticking = false;
});
ticking = true;
}
}
window.addEventListener('scroll', updateProgress);
CSS 实现顶部固定进度条
进度条要始终贴顶、不遮挡内容、不影响布局流。关键样式是:position: fixed + top: 0 + z-index: 9999。宽度用 width 动态控制,但必须设 height 和 background 才能看见。
容易忽略的点:
立即学习“前端免费学习笔记(深入)”;
- 没设
left: 0和right: 0,导致进度条只占视口部分宽度 - 父容器有
overflow-x: hidden,把进度条裁掉 - 没加
transition: width 0.2s ease,进度跳变生硬
推荐基础 CSS:
#progress-bar {
position: fixed;
top: 0;
left: 0;
height: 3px;
background: #4a6fa5;
width: 0%;
z-index: 9999;
transition: width 0.2s ease;
}
兼容移动端 touch 滚动
iOS Safari 和部分安卓 WebView 中,scroll 事件在触摸滚动时可能不连续或延迟触发。需同时监听 touchmove 事件,并阻止默认行为(仅当需要自定义滚动逻辑时才这么做)。
更稳妥的做法是:不阻止默认行为,改用 passive: true 选项提升性能,同时确保 touchmove 也调用同一更新函数:
window.addEventListener('touchmove', updateProgress, { passive: true });
注意:passive: true 表示你承诺不会在该监听器里调用 preventDefault(),否则 Chrome 会报警告;若页面本身有下拉刷新等逻辑,需单独处理冲突。
避免首屏加载时进度条闪动
页面刚加载完成时,DOM 尚未完全渲染(尤其含图片、字体、异步内容),scrollHeight 可能被低估,导致初始进度条宽度突变为 100% 后又回落——肉眼可见“闪一下”。
解决方法只有两个:
- 在
DOMContentLoaded后延迟 100ms 再初始化进度条(给渲染留出余量) - 监听
load事件(等所有资源加载完),但会延长首次显示时间 - 更优解:用
ResizeObserver监听document.documentElement尺寸变化,动态修正进度值(适合长图文+懒加载场景)
简单项目选第一种即可,复杂页面建议加上 ResizeObserver 回调重新校准。
真正难的不是画一条线,而是让这条线在各种设备、各种加载节奏、各种 CSS 布局下都稳住数值和位置。尤其是 scrollHeight 的取值时机和对象,一错就全偏。



















