上拉加载时怎么判断滚动到底部了?

桠豪~ 阅读 28

我在做移动端列表的上拉加载,监听 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();
  }
};
我来解答 赞 4 收藏
二维码
手机扫码查看
1 条解答
书生シ子睿
先检查一下你的容器是不是设置了 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)就能稳住。
点赞 4
2026-02-24 10:17