如何在倒计时运行时通过输入操作正确重置setInterval计时器
问题修复方案
核心逻辑非常直接:每次触发点击启动新计时前,优先清除已存在的旧定时器实例,从根源避免多个定时器同时运行导致的计时异常。
原有问题原因
原代码每次点击按钮执行startTimer时,都会直接创建新的setInterval任务,没有对之前已经在运行的定时器做清理,最终会出现多个定时器同时调用tick(),导致倒计时跳数、速度变快的问题。另外原写法里setInterval("tick()", 1000)传字符串的方式会触发隐式eval,存在性能和作用域隐患,建议直接传入函数引用。
修复后完整代码
JS部分
var timeInSecs; var ticker; function startTimer() { // 核心修复:启动新定时器前,先清除已存在的旧定时器 if (ticker) { clearInterval(ticker); } var newValues = document.getElementById("mm").value; var secondInput = document.getElementById("secondsdown").value; if (secondInput == null || secondInput == '') { secondInput = '0'; } // 补空值兜底,避免输入为空时parseInt返回NaN var secs = 60 * parseInt(newValues || 0); timeInSecs = parseInt(secs + parseInt(secondInput || 0)); // 直接传函数引用,不要传字符串执行 ticker = setInterval(tick, 1000); // 启动后立刻执行一次tick,避免首次点击要等1秒才更新显示 tick(); } function tick() { var secs = timeInSecs; document.getElementById("secondsdown").value = secs; if (secs > 0) { timeInSecs--; } else { clearInterval(ticker); ticker = null; // 归0后清空定时器id标记 } var days = Math.floor(secs / 86400); secs %= 86400; var hours = Math.floor(secs / 3600); secs %= 3600; var mins = Math.floor(secs / 60); secs %= 60; var pretty = ((hours < 10) ? "0" : "") + hours + ":" + ((mins < 10) ? "0" : "") + mins + ":" + ((secs < 10) ? "0" : "") + secs; document.getElementById("countdown").innerHTML = pretty; }
HTML部分(修正了原标签多余的分号,结构无变化)
<div class="master-slider ms-skin-light-2" id="masterslider"> <div class="ms-slide"> <input type="number" id="secondsdown"/> <span id="countdown"></span> <input id="mm" placeholder="分钟" type="number"/> <input type="button" value="Click" onclick="startTimer()"/> </div> </div><!-- end of masterslider1 -->
关键修复点说明
- 进入
startTimer第一时间判断是否存在正在运行的定时器,存在就先调用clearInterval清理,保证同一时间永远只有一个定时器在运行 - 倒计时归0清理定时器后,把
ticker变量置为null,方便下次启动时判断状态 - 新增空值兜底,避免分钟/秒输入为空时
parseInt返回NaN导致整个计时逻辑失效 - 启动定时器后立刻调用一次
tick(),解决原代码点击后要等1秒才会刷新显示的体验问题 - 替换
setInterval的字符串传参为函数引用,避免隐式eval的隐患
内容的提问来源于stack exchange,提问作者Mr Rabbit
相关产品推荐
相关产品推荐

