Hey friends!
I know I’m late today, sorry😥, but better late than never right? 😉
So this Wednesday, we’re doing something a bit different, making two mini-apps in one:
- A Stopwatch (Start, Pause, Reset)
- A Countdown Timer (Set time, Start, Reset)
Timers are a great way to practice real-time updates, DOM manipulation and understanding JavaScript’s event loop with setInterval
and clearInterval
.
Understand JavaScript Timers
In JavaScript, timers let us run code repeatedly or after a delay:
setInterval(callback, delay) // Runs callback every delay ms
clearInterval(id) // Stops the interval
setTimeout(callback, delay) // Runs callback once after delay ms
clearTimeout(id) // Cancels the timeout
For our apps:
- The Stopwatch will use
setInterval
to update every second - The Countdown will also use
setInterval
, but decrease time until it hits 0
HTML
<div class="container">
<h1>⏱ Timer Playground</h1>
<!-- Stopwatch -->
<section>
<h2>Stopwatch</h2>
<div id="stopwatch-display">00:00:00</div>
<button id="start-stopwatch">Start</button>
<button id="pause-stopwatch">Pause</button>
<button id="reset-stopwatch">Reset</button>
</section>
<hr>
<!-- Countdown -->
<section>
<h2>Countdown Timer</h2>
<input type="number" id="countdown-input" placeholder="Seconds" min="1">
<div id="countdown-display">00:00</div>
<button id="start-countdown">Start</button>
<button id="reset-countdown">Reset</button>
</section>
</div>
CSS
body {
font-family: Arial, sans-serif;
text-align: center;
background: #f4f4f4;
}
.container {
background: white;
padding: 20px;
max-width: 400px;
margin: auto;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
h1, h2 {
color: #333;
}
#stopwatch-display, #countdown-display {
font-size: 2em;
margin: 10px 0;
}
button {
margin: 5px;
padding: 8px 12px;
border: none;
background: #007bff;
color: white;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
input {
padding: 8px;
width: 100px;
text-align: center;
}
JavaScript
// STOPWATCH
let stopwatchInterval;
let stopwatchSeconds = 0;
const stopwatchDisplay = document.getElementById('stopwatch-display');
const startStopwatch = document.getElementById('start-stopwatch');
const pauseStopwatch = document.getElementById('pause-stopwatch');
const resetStopwatch = document.getElementById('reset-stopwatch');
function formatTime(sec) {
const h = String(Math.floor(sec / 3600)).padStart(2, '0');
const m = String(Math.floor((sec % 3600) / 60)).padStart(2, '0');
const s = String(sec % 60).padStart(2, '0');
return `${h}:${m}:${s}`;
}
startStopwatch.addEventListener('click', () => {
clearInterval(stopwatchInterval);
stopwatchInterval = setInterval(() => {
stopwatchSeconds++;
stopwatchDisplay.textContent = formatTime(stopwatchSeconds);
}, 1000);
});
pauseStopwatch.addEventListener('click', () => {
clearInterval(stopwatchInterval);
});
resetStopwatch.addEventListener('click', () => {
clearInterval(stopwatchInterval);
stopwatchSeconds = 0;
stopwatchDisplay.textContent = '00:00:00';
});
// COUNTDOWN
let countdownInterval;
let countdownSeconds;
const countdownInput = document.getElementById('countdown-input');
const countdownDisplay = document.getElementById('countdown-display');
const startCountdown = document.getElementById('start-countdown');
const resetCountdown = document.getElementById('reset-countdown');
startCountdown.addEventListener('click', () => {
clearInterval(countdownInterval);
countdownSeconds = parseInt(countdownInput.value);
if (isNaN(countdownSeconds) || countdownSeconds <= 0) {
countdownDisplay.textContent = 'Invalid!';
return;
}
countdownDisplay.textContent = formatCountdown(countdownSeconds);
countdownInterval = setInterval(() => {
countdownSeconds--;
countdownDisplay.textContent = formatCountdown(countdownSeconds);
if (countdownSeconds <= 0) {
clearInterval(countdownInterval);
alert('Time’s up!');
}
}, 1000);
});
resetCountdown.addEventListener('click', () => {
clearInterval(countdownInterval);
countdownDisplay.textContent = '00:00';
});
function formatCountdown(sec) {
const m = String(Math.floor(sec / 60)).padStart(2, '0');
const s = String(sec % 60).padStart(2, '0');
return `${m}:${s}`;
}
Live Demo
👉 Try the Timer Playground on CodePen
Challenge for You 🚀
- Add a Lap Feature to the stopwatch
- Make the countdown timer pause & resume
- Style it with a dark mode
🙋🏽♀️ Over to You!
Let me know if you do the challenge! I’d like to see yours! Connect with me on GitHub
Was this tutorial helpful? Got questions? Or any insight to help me write better tutorials? Let me know in the 💬!
That’s it for today’s midweek mini tutorial!
I’m keeping things light, fun and useful; one small project at a time.
*If you enjoyed this, leave a 💬 or 🧡 to let me know. *
And if you’ve got an idea for something you’d like me to try out next Wednesday, drop it in the comments. 👇
Follow me to see more straight-forward and short tutorials like this 🙂
If you are curious about what I do, check out my Portfolio
🙂
Web trails
You can also find me here on LinkedIn
or here X (Twitter)
✍🏾 I’m documenting my learning loudly every Wednesday. Follow along if you’re learning JavaScript too!
Let’s keep learning together!
💡 Tip
Try rebuilding this app from memory after going through it once. That’s the best way to reinforce what you’ve learned.
See you next Wednesday 🚀