JavaScript如何实现div滚动完成后高亮元素,无需依赖setTimeout
问题核心原因
你之前的封装逻辑不生效是因为scrollTo如果启用了平滑滚动属于异步操作,即使是即时滚动,也需要等待浏览器完成重绘后元素位置才会稳定,你的Promise没有等待上述过程完成就直接执行了resolve,导致高亮逻辑执行时滚动还未完成、元素位置还未更新,因此没有效果。
方案1:使用scrollend事件(推荐,现代浏览器原生支持)
scrollend是浏览器原生提供的滚动结束事件,滚动完全结束后触发,不需要手动判断状态,兼容性覆盖Chrome 108+、Firefox 109+、Safari 15.4+,是目前最稳定的实现方式。
const commentDiv = document.getElementById('commentsDiv'); const onScrollEnd = () => { // 执行高亮逻辑 decorateMarker(markerObj); // 执行完成后移除监听,避免重复触发 commentDiv.removeEventListener('scrollend', onScrollEnd); }; commentDiv.addEventListener('scrollend', onScrollEnd); // 触发滚动,可根据需要加behavior配置是否平滑滚动 commentDiv.scrollTo({ top: scrollValue, left: 0, // behavior: 'smooth' 开启平滑滚动的话保留这行 });
方案2:兼容旧浏览器的Promise封装方案
如果需要兼容不支持scrollend的旧版本浏览器,可以通过requestAnimationFrame监听滚动位置的变化,当连续两帧滚动位置一致时判定为滚动结束:
// 封装滚动结束等待方法 function waitForScrollFinish(element) { return new Promise((resolve) => { let lastScrollTop = element.scrollTop; const checkScroll = () => { const currentScrollTop = element.scrollTop; if (currentScrollTop === lastScrollTop) { // 滚动位置不再变化,判定结束 resolve(); return; } lastScrollTop = currentScrollTop; requestAnimationFrame(checkScroll); }; requestAnimationFrame(checkScroll); }); } // 调用示例 const commentDiv = document.getElementById('commentsDiv'); commentDiv.scrollTo(0, scrollValue); waitForScrollFinish(commentDiv).then(() => { decorateMarker(markerObj); });
方案3:无平滑滚动场景的极简方案
如果你不需要平滑滚动,只是用scrollTop直接设置滚动位置,只需要等待浏览器完成一次重绘即可,用两次requestAnimationFrame就可以实现:
const commentDiv = document.getElementById('commentsDiv'); commentDiv.scrollTop = scrollValue; // 两次requestAnimationFrame确保浏览器已完成滚动对应的DOM渲染 requestAnimationFrame(() => { requestAnimationFrame(() => { decorateMarker(markerObj); }); });
内容的提问来源于stack exchange,提问作者help
相关产品推荐
相关产品推荐

