上拉加载时怎么判断滚动到底部了?
我在做移动端列表的上拉加载,监听 scroll 事件后用 scrollTop + clientHeight >= scrollHeight 判断是否到底,但有时候会触发多次,甚至在还没到底的时候就触发了,这是为啥?
我试过加防抖,也试过用 Math.abs(scrollTop + clientHeight - scrollHeight) < 10 这种容差判断,但体验还是不太稳定,尤其在 iOS 上特别明显。
下面是我现在的监听逻辑:
const handleScroll = () => {
const { scrollTop, clientHeight, scrollHeight } = containerRef.current;
if (scrollTop + clientHeight >= scrollHeight - 5) {
loadMore();
}
};
overflow: auto或者scroll,而且必须是块级元素,不然scrollHeight可能算得不对,尤其在 iOS Safari 里,有些情况下它会把滚动容器算成 body 而不是你指定的 div。然后重点来了:你用
scrollTop + clientHeight >= scrollHeight - 5这个判断在滚动过程中会反复触发,因为滚动是离散采样的,尤其在 iOS 上滚动惯性大,一次滚动里可能多次跨过这个阈值,或者因为浮点精度问题(比如实际是 999.9999 而 scrollHeight 是 1000)导致提前触发。建议改成这样:
let isLoading = false;const handleScroll = () => {const { scrollTop, clientHeight, scrollHeight } = containerRef.current;if (isLoading) return;if (scrollTop + clientHeight >= scrollHeight - 10) {isLoading = true;loadMore().finally(() => { isLoading = false; });}};这里加个
isLoading标志位防重复触发,比防抖靠谱多了——防抖只能延时,但滚得快的时候可能一滑就跳过阈值好几次,防抖根本挡不住。另外,iOS 上 sometimes 会因为虚拟键盘弹出、地址栏隐藏导致
clientHeight变化,建议监听resize事件时重新计算或者刷新状态。如果还是不稳,再试试监听
touchend+scrollend(非标准但 iOS 上好用)做二次确认,不过现在主流方案基本靠isLoading+ 合理的阈值(比如-10)就能稳住。