如何在指定counter-value div进入视口时触发计数器且仅执行一次
解决方案:仅当计数器元素进入视口时执行一次动画
要实现这个需求,最佳方案是使用浏览器原生的Intersection Observer API——它专门用于监听元素是否进入视口,性能远优于传统的scroll事件监听,还能轻松确保代码只执行一次。
下面是修改后的完整代码:
// 封装原来的计数器逻辑为独立函数 function initCounters() { try { const counter = document.querySelectorAll(".counter-value"); const speed = 2500; // 数值越小,动画越慢 counter.forEach((counter_value) => { const updateCount = () => { const target = +counter_value.getAttribute("data-target"); const count = +counter_value.innerText; // 计算增量,确保至少为1避免无限循环 let inc = target / speed; if (inc < 1) { inc = 1; } // 更新计数直到达到目标值 if (count < target) { counter_value.innerText = (count + inc).toFixed(0); setTimeout(updateCount, 1); } else { counter_value.innerText = target; } }; updateCount(); }); } catch (err) { console.error("计数器初始化失败:", err); // 可选:添加错误日志便于调试 } } // 创建Intersection Observer实例,监听元素进入视口 let countersInitialized = false; // 标记是否已经执行过计数器逻辑 const observer = new IntersectionObserver((entries) => { // 检查是否有任意一个计数器元素进入视口,且尚未初始化 if (!countersInitialized && entries.some(entry => entry.isIntersecting)) { initCounters(); countersInitialized = true; // 标记为已执行,避免重复触发 observer.disconnect(); // 断开观察,释放资源 } }, { threshold: 0.1 // 当元素有10%进入视口时触发,可根据需求调整 }); // 开始观察所有计数器元素 document.querySelectorAll(".counter-value").forEach(el => { observer.observe(el); }); // 额外处理:如果页面加载时元素已经在视口内,立即执行 if (!countersInitialized) { const isAnyCounterInViewport = Array.from(document.querySelectorAll(".counter-value")).some(el => { const rect = el.getBoundingClientRect(); return rect.top < window.innerHeight && rect.bottom > 0; }); if (isAnyCounterInViewport) { initCounters(); countersInitialized = true; observer.disconnect(); } }
代码说明:
- 封装逻辑:把原来的计数器动画代码放到
initCounters()函数里,便于复用和控制执行时机 - Intersection Observer:监听所有
.counter-value元素,当其中任意一个进入视口(达到设置的阈值)时,执行计数器逻辑 - 防止重复执行:用
countersInitialized标志位确保代码只运行一次,执行后立即断开观察释放浏览器资源 - 边界处理:额外检查页面加载时元素是否已经在视口内,避免错过触发时机
- 错误优化:在catch块中添加了错误日志,便于调试(可选,你可以根据需要移除)
为什么不用scroll事件?
传统的scroll事件会在用户滚动时频繁触发,需要手动节流,性能较差;而Intersection Observer是浏览器原生优化的API,只会在元素进入/离开视口时触发,性能更优,代码也更简洁。
内容的提问来源于stack exchange,提问作者Sohaib Butt
相关产品推荐
相关产品推荐

