如何基于视口与div位置计算文章阅读进度百分比?
博客文章阅读进度百分比计算问题修复
问题场景
我在搭建博客时需要实现**显示访客当前阅读文章深度(百分比)**的功能,通过追踪浏览器底部边界与文章位置对比计算。页面有一个60vh高的顶部横幅(含标题、元数据),文章在横幅下方,无固定高度且未设置overflow: scroll。
当前实现代码如下:
const getScrollPercent = () => { const prose = document.querySelector<HTMLDivElement>('.prose') if (prose) { // closest way I got the actual div's top side) const proseTop = prose.getBoundingClientRect().top + document.documentElement.scrollTop const proseBottom = proseTop + prose.offsetHeight const browserBottom = window.scrollY + window.innerHeight if (browserBottom < proseTop) { return 0 } else if (browserBottom > proseTop && browserBottom < proseBottom) { return (browserBottom / proseBottom) * 100 } else { return 100 } } else { return 0 } }
但实际表现存在问题:.prose未进入视口时显示0%符合预期,但刚进入视口第一像素时,百分比直接跳至约24%,滚动到文章底部时能正常显示100%。
补充HTML结构(Nuxt/VueJS环境):
<nav> <!-- Contains some links--> </nav> <main> <header> <h1>Some article title with a banner in background</h1> <div class="meta"> <!-- Some meta infos (tags, creation date, etc) --> </div> </header> <div class="progress"> <span class="global"> {{ getScrollPercent }}% </span> </div> <article> <!-- lots of <p> and <h2> to <h6> tags, about 2200px high) !--> </article> </main>
问题原因
核心问题出在进度计算逻辑:当前用browserBottom / proseBottom * 100计算,而proseBottom是从页面顶部到文章底部的总距离,当浏览器底部刚碰到文章顶部时,browserBottom已经等于proseTop(即顶部横幅的高度),此时的比值直接导致百分比跳变。正确的计算应该是浏览器底部进入文章区域的距离,除以文章自身的总高度,才能从0%开始递增。
修复方案
调整计算逻辑,用(browserBottom - proseTop)得到已阅读的文章高度,再除以文章自身高度prose.offsetHeight,最后乘以100得到百分比:
const getScrollPercent = () => { const prose = document.querySelector<HTMLDivElement>('.prose') if (prose) { const proseTop = prose.getBoundingClientRect().top + document.documentElement.scrollTop const proseHeight = prose.offsetHeight const browserBottom = window.scrollY + window.innerHeight if (browserBottom < proseTop) { return 0 } else if (browserBottom > proseTop && browserBottom < proseTop + proseHeight) { // 计算已阅读部分占文章总高度的比例 const scrolledIntoProse = browserBottom - proseTop return (scrolledIntoProse / proseHeight) * 100 } else { return 100 } } else { return 0 } }
额外优化
如果需要处理百分比的小数精度,可以用Math.round()或toFixed()格式化结果,例如:
return Math.round((scrolledIntoProse / proseHeight) * 100)
内容的提问来源于stack exchange,提问作者Jaeger
相关产品推荐
相关产品推荐

