Zum Inhalt springen

Build a Stopwatch & Countdown with JavaScript

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 🚀

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert