How to Fix It Timer Displaying Delay in JavaScript
If you’ve built a JavaScript countdown timer and noticed it doesn’t align with real time, you’re not alone. In this article, we’ll diagnose why delays occur in timers like this JSFiddle example and implement a fix. We’ll also enhance the timer with practical features like pause/resume, custom durations, and a progress bar.
Why the Delay Happens
The original code uses setInterval to decrement a sec variable every second. However, setInterval isn’t perfectly precise—it schedules tasks approximately every 1000ms. Browser operations, background tabs, or heavy scripts can delay these intervals. Over time, these small delays compound, causing the timer to drift significantly.
For example:
- A 10-minute timer might show 01:10 remaining after 10 real minutes.
- A 30-minute timer could lag by 5+ minutes.
The root cause: The timer relies on the number of interval cycles rather than actual elapsed time.
Use System Time for Accuracy
Instead of decrementing sec blindly, calculate the remaining time using the system clock. Here’s the revised code:
let initialSec = 600; // 10 minutes in seconds
let countDiv = document.getElementById("timer");
let startTime = Date.now();
let countDown = setInterval(updateTimer, 1000);
function updateTimer() {
const currentTime = Date.now();
const elapsed = Math.floor((currentTime - startTime) / 1000);
const remaining = initialSec - elapsed;
const min = String(Math.floor(remaining / 60)).padStart(2, '0');
const remSec = String(remaining % 60).padStart(2, '0');
countDiv.innerHTML = `${min}:${remSec}`;
if (remaining <= 0) {
clearInterval(countDown);
countDiv.innerHTML = 'Countdown Done!';
}
}
Key changes:
- Track
startTimeusingDate.now()for accuracy. - Calculate
elapsedtime dynamically. - Use
padStartto format minutes and seconds.
Enhancing the Timer with Features
Pause/Resume Functionality
Add buttons to pause and resume the timer:
<button id="pauseBtn">Pause</button> <button id="resumeBtn" style="display: none;">Resume</button>
Run HTML
let isPaused = false;
let remainingOnPause;
document.getElementById('pauseBtn').addEventListener('click', pauseTimer);
document.getElementById('resumeBtn').addEventListener('click', resumeTimer);
function pauseTimer() {
clearInterval(countDown);
isPaused = true;
remainingOnPause = initialSec - Math.floor((Date.now() - startTime) / 1000);
document.getElementById('pauseBtn').style.display = 'none';
document.getElementById('resumeBtn').style.display = 'inline';
}
function resumeTimer() {
isPaused = false;
initialSec = remainingOnPause;
startTime = Date.now();
countDown = setInterval(updateTimer, 1000);
document.getElementById('pauseBtn').style.display = 'inline';
document.getElementById('resumeBtn').style.display = 'none';
}
Custom Duration Input
Let users set their own timer duration:
<input type="number" id="minutesInput" placeholder="Minutes" value="10"> <button id="startBtn">Start</button>
Run HTML
document.getElementById('startBtn').addEventListener('click', () => {
const minutes = parseInt(document.getElementById('minutesInput').value);
if (!isNaN(minutes)) {
initialSec = minutes * 60;
startTime = Date.now();
if (countDown) clearInterval(countDown);
countDown = setInterval(updateTimer, 1000);
}
});
Progress Bar
Visualize time progression with a CSS-based bar:
<div class="progress-container"> <div id="progressBar" class="progress-bar"></div> </div>
Run HTML
.progress-container {
width: 100%;
height: 4px;
background: #ddd;
margin-top: 10px;
}
.progress-bar {
height: 100%;
background: crimson;
width: 0%;
transition: width 0.3s ease;
}
Update the progress in updateTimer():
const progress = (remaining / initialSec) * 100;
document.getElementById('progressBar').style.width = `${progress}%`;
Final Thoughts
Timers relying solely on setInterval are prone to drift. By anchoring calculations to the system clock (Date.now()), we ensure accuracy regardless of interval delays. Adding features like pause/resume, custom inputs, and visual feedback makes the timer more robust and user-friendly.