React中递归函数内scrollBy执行不符合预期问题排查
问题原因及解决方案
核心原因
- 页面高度获取错误:你用
document.body.scrollHeight计算页面总高度,但在现代浏览器标准模式(带DOCTYPE声明)下,document.body.scrollHeight通常仅等于视口高度,正确的页面总高度应该从document.documentElement.scrollHeight获取。这导致第一次滚动后,代码错误判断剩余滚动距离不足20px,直接停止递归调用。 - 滚动调度间隔不合理:
setTimeout(scrollBottom, 0)的间隔为0,浏览器事件循环可能无法及时调度后续滚动任务,同时滚动效果也会过于生硬。
另外需确认触发区域定位:如果触发区域是absolute定位,页面滚动时它会跟着移动,导致鼠标离开触发区域,overBottomTrigger.current变为false也会停止滚动——建议将触发区域设为fixed定位,固定在页面底部。
修复后的代码
import { useCallback, useRef, ReactNode } from 'react'; import styles from './AutoScroll.module.css'; export interface IAutoScrollProps { children: ReactNode; } const AutoScroll = ({ children }: IAutoScrollProps) => { const bottomTriggerClass = `${styles['bottom-trigger']} ${styles['trigger']} ${styles['horizontal']}`; const overBottomTrigger = useRef(false); const scrollBottom: () => void = useCallback(() => { window.scrollBy(0, 10); if (!overBottomTrigger.current) return; // 兼容不同浏览器的页面总高度计算 const totalPageHeight = Math.max( document.body.scrollHeight, document.documentElement.scrollHeight ); const remainingScroll = totalPageHeight - window.innerHeight - window.scrollY; if (remainingScroll < 20) return; // 用16ms间隔(接近60fps)实现平滑滚动 setTimeout(scrollBottom, 16); }, []); const onBottomTriggerMouseEnter = useCallback(() => { overBottomTrigger.current = true; scrollBottom(); }, [scrollBottom]); const onBottomTriggerMouseLeave = useCallback(() => { overBottomTrigger.current = false; }, []); return ( <div> <section className="autoScroll-container p-relative"> {children} </section> <span className={bottomTriggerClass} onMouseEnter={onBottomTriggerMouseEnter} onMouseLeave={onBottomTriggerMouseLeave} ></span> </div> ); }; export default AutoScroll;
额外样式建议(确保触发区域正常工作)
在CSS模块中添加:
.bottom-trigger { position: fixed; bottom: 0; left: 0; width: 100%; height: 40px; /* 调整为适合的触发区域高度 */ opacity: 0; /* 透明不遮挡内容 */ pointer-events: auto; /* 确保能捕获鼠标事件 */ z-index: 999; /* 防止被其他元素遮挡 */ }
内容的提问来源于stack exchange,提问作者Андрей
相关产品推荐
相关产品推荐

