如何避免两个互相调用的onscroll事件监听器引发滚动卡顿问题?
问题原因
双向滚动事件形成触发循环:滚动表格触发的scroll事件会修改自定义滚动条的scrollLeft,这一赋值操作又会触发滚动条的scroll事件,反过来修改表格的scrollLeft,循环往复触发大量不必要的重排计算,最终导致掉帧。
解决方案
这里提供两种成熟的实现方案,都可以彻底切断循环触发:
方案1:标志位锁方案(推荐,可控性强)
通过一个同步锁标记拦截联动过程中触发的无效滚动事件,从根源避免循环:
// 全局或闭包作用域下定义同步锁标记 let isSyncing = false; const tableContainer = document.getElementById('table-container-div'); const fauxScrollbar = document.getElementById('faux-scrollbar'); tableContainer.addEventListener('scroll', () => { // 同步过程中触发的滚动事件直接跳过 if (isSyncing) return; // 加锁,禁止后续联动触发的滚动事件执行同步逻辑 isSyncing = true; fauxScrollbar.scrollLeft = tableContainer.scrollLeft; // 下一次渲染周期重置锁,保证本次联动的所有事件都被过滤 requestAnimationFrame(() => { isSyncing = false; }); }); fauxScrollbar.addEventListener('scroll', () => { if (isSyncing) return; isSyncing = true; tableContainer.scrollLeft = fauxScrollbar.scrollLeft; requestAnimationFrame(() => { isSyncing = false; }); });
方案2:值比对方案(代码更简洁)
提前判断两个元素的滚动位置是否已经一致,避免重复赋值触发额外事件:
const tableContainer = document.getElementById('table-container-div'); const fauxScrollbar = document.getElementById('faux-scrollbar'); tableContainer.addEventListener('scroll', () => { if (fauxScrollbar.scrollLeft !== tableContainer.scrollLeft) { fauxScrollbar.scrollLeft = tableContainer.scrollLeft; } }); fauxScrollbar.addEventListener('scroll', () => { if (tableContainer.scrollLeft !== fauxScrollbar.scrollLeft) { tableContainer.scrollLeft = fauxScrollbar.scrollLeft; } });
方案选择建议
- 如果存在其它逻辑会修改两个元素的滚动位置,优先选择方案1,逻辑更可控
- 如果是简单的双向同步场景,方案2代码更简洁,完全满足需求
内容的提问来源于stack exchange,提问作者Codemonkey
相关产品推荐
相关产品推荐

