JS多元素data属性计数实现 各元素独立停止在对应data-num值
问题描述
页面存在多个带.number类的div元素,每个元素配置了取值互不相同的data-num自定义属性,需要通过JavaScript实现数字从0开始递增的计数动画,最终将计数结果输出到对应div中,且每个元素计数到自身data-num对应的数值时自动停止。
原代码运行时出现异常:计数器无法在对应元素的目标值处停止,会无限持续递增,所有元素的计数状态互相干扰。
原实现代码
JavaScript 代码
const numbers = document.querySelectorAll(".number"); console.log(numbers); let counter = 0; setInterval(() => { numbers.forEach((number) => { if(counter === number.dataset.num){ clearInterval(); }else{ counter += 1; number.innerHTML = counter + "%"; } }) }, 20);
HTML 代码
<div class="skill-level position-absolute"> <div class="outer"> <div class="inner d-flex align-items-center justify-content-center"> <div class="number" data-num="95"></div> </div> </div> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="160px" height="160px"> <defs> <linearGradient id="GradientColor"> <stop offset="0%" stop-color="#e91e63" /> <stop offset="100%" stop-color="#673ab7" /> </linearGradient> </defs> <circle cx="80" cy="80" r="70" stroke-linecap="round" /> </svg> </div> <div class="skill-level position-absolute"> <div class="outer"> <div class="inner d-flex align-items-center justify-content-center"> <div class="number" data-num="70"></div> </div> </div> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="160px" height="160px"> <defs> <linearGradient id="GradientColor"> <stop offset="0%" stop-color="#e91e63" /> <stop offset="100%" stop-color="#673ab7" /> </linearGradient> </defs> <circle cx="80" cy="80" r="70" stroke-linecap="round" /> </svg> </div>
故障原因
- 全局仅声明了一个
counter变量,所有计数元素共享同一个计数状态,无法匹配每个元素独立的目标值 clearInterval()调用时没有传入定时器返回的唯一ID,无法终止正在运行的定时器- 遍历逻辑存在缺陷:每次定时器触发时会给所有元素同步累加同一个全局计数器,即便小目标值的元素达到阈值,全局计数器仍会持续累加,导致计数无限上涨
修复实现
核心思路是给每个.number元素绑定独立的计数变量、独立的定时器实例,计数状态互不干扰,计数到自身目标值时清除对应定时器即可。修复后的JavaScript代码如下:
const numbers = document.querySelectorAll(".number"); numbers.forEach(number => { // 每个元素维护独立的计数初始值 let counter = 0; // 读取当前元素的目标计数,dataset返回字符串类型,显式转为数字 const target = Number(number.dataset.num); // 为每个元素创建独立定时器,保存定时器ID用于后续清除 const countTimer = setInterval(() => { if (counter >= target) { // 达到目标值,清除当前元素对应的定时器 clearInterval(countTimer); // 兜底确保最终显示值和目标值完全一致 number.textContent = `${target}%`; return; } counter++; number.textContent = `${counter}%`; }, 20); })
上述实现无需修改原有HTML结构,每个元素的计数动画独立运行,到达自身目标值后自动停止,不会出现无限递增、计数互相干扰的问题。
内容的提问来源于stack exchange,提问作者Mohamad
相关产品推荐
相关产品推荐

