Howdy folks, In this tutorial, you will learn how to build a Javascript countdown timer. Our countdown timer will be in the format hours + ':' + minutes + ':' + seconds. The user will be able to set the countdown time in hours, minutes, or seconds as he/she likes. Then also there will be buttons to stop and reset the countdown timer. 

Before jumping right in first, take a look at the demo here on Codepen --> Countdown Timer

You can download the completed project from GitHub --> Countdown Timer Javascript

Or you can get the final code at the end of each section too...

Prerequisites

I assume you are already familiar with basic Javascript (ES6). And you should have a decent understanding of HTML5 and CSS3. 

But anyway feel free to follow along, I will try my best to keep this tutorial simple.

I am going to use "visual studio code" as my text editor. You are free to use whatever text editor you like. And finally, make sure you have installed Live Server by Ritwick Dey in visual studio code. If you don't have Live Server installed, then:

  • Open visual studio code
  • Goto Extensions tab (Ctrl + Shift + X)
  • Search for "Live Server" by Ritwick Dey
  • Then Install it

Now Let's BreakDown the Logic...

Breaking Down the Logic

Logic is quite simple.

  • We have a .container that holds everything.
    • Then inside that container, we had a .form. Inside that form:
      • There is an input box with class .time-input to enter the countdown time.
      • Then there is a dropdown box called .format to select the format (hours or minutes or seconds).
      • Then a submit button called SET with class .set-btn.
    • <p> tag with class .countdown to display the countdown text.
    • Finally, a <div> with .buttons class. Inside that there are two buttons:
      • One called .stop-btn, to pause the countdown timer.
      • And another called .reset-btn, to reset the countdown.

html layout breakdown

Initially our .stop-btn and .reset-btn are disabled.

When a user types the countdown time in the input box and then hits the .set-btn, the countdown starts working. Also our .set-btn becomes disable and .stop-btn gets enable.

When the user hits on the .stop-btn, the countdown freezes, and the text in the .stop-btn changes to "PLAY". Also the .reset-btn gets enabled.

When the user hits on the .reset-btn, everything goes back to its initial state.

Enough talk. Now let's get started...

Initial Setups

Create a folder structure similar to below:

folder structure

  • First, create the root folder which holds everything and name it as "CountDown Timer"
  • Then open this folder inside visual studio code.
  • Then directly inside this root folder, create a file named "index.html" --> This holds our HTML.
  • Then again directly inside our root folder, create two folders:
    • One named "css" --> Inside this folder, create a file named "styles.css".
    • Then another folder named "js" --> Inside this folder, create a file named "script.js".

That's it. Now you should have a folder structure similar to above. Now the HTML part...

HTML

The HTML file will be exactly as I explained earlier. So If you are willing for some sort of adventure, then try to code it yourself. Then come back and make sure the code you typed is exactly like the following. 

Or just open the "index.html" and type the following:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" type="text/css" href="css/styles.css"/>
  <title>CountDown Timer</title>
</head>
<body>

  <div class="container">
    <!-- form -->
    <form class="form">
      <!-- countdown time input field -->
      <input type="number" min="1" class="time-input" placeholder="Enter Countdown">
      <!-- options -->
      <select name="format">
        <option value="hours">Hours</option>
        <option value="minutes">Minutes</option>
        <option value="seconds">Seconds</option>
      </select>
      <!-- submit button or set button -->
      <button type="submit" class="set-btn">SET</button>
    </form>
    <!-- countdown text -->
    <p class="countdown">00 : 00 : 00</p>

    <!-- buttons -->
    <div class="buttons">
      <button class="stop-btn" disabled>STOP</button>
      <button class="reset-btn" disabled>RESET</button>
    </div>
  </div>

  <script type="text/javascript" src="js/script.js"></script>
</body>
</html>

CSS

Open the "styles.css" file you created and copy/type the following:

/* Roboto font */
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');

/* common styles */
* {
  padding: 0;
  margin: 0;
  font-size: 1rem;
  font-family: 'Roboto', sans-serif;
}

body {
  width: 100vw;
  height: 100vh;
  background-color: #111;
  overflow: hidden;
  display: flex;
  justify-content: center;
  text-align: center;
  color: #00ff44;
}

button {
  padding: 7px 15px;
  border: none;
  outline: none;
  border-radius: 4px;
}

button:hover {
  cursor: pointer;
  background-color: #ffff00;
}
/* common styles end */

/* form */
.form {
  margin: 90px 0px 20px 0px;
}

.time-input {
  padding: 10px 7px;
  border: none;
  outline: none;
  border-radius: 4px;
}

select[name='format'] {
  padding: 10px;
  border: none;
  outline: none;
  border-radius: 4px;
  margin: 0px 10px;
  background-color: #00ff44;
}

.set-btn {
  background-color: #00ff44;
}
/* form styles end */

/* countdown */
.countdown {
  font-size: 6.5rem;
  margin-top: 20px;
}
/* countdown styles end */

/* buttons */
.stop-btn {
  padding: 10px 20px;
  background-color: #ff6344;
}

.reset-btn {
  margin-top: 10px;
  padding: 10px 20px;
  background-color: rgb(0, 174, 255);
}
/* buttons styles end */

HTML and CSS are not our main focus here. So I am not going to explain it.

If you take a look at the browser, then you will see the page with the styles. But the timer doesn't work. Now let's make it work.

Javascript

Open the "script.js" file and copy the following comments:

/* grab necessary elements */

/* grab necessary elements ends */ 


/* global variables and constants*/

/* global variables ends */


/* .stop-btn click listener */

/* .stop-btn click listener ends */


/* .reset-btn click listener */

/* .reset-btn click listener ends */


/* .form submit listener */

/* .form submit listener ends */


/* setCountDown function */

/* setCountDown function ends */


/* resetCountDown function */

/* resetCountDown function ends */

This comments will tell you what are the things needed to be done.

Basically, these are the things that need to be done:

  • Grab everything needed
  • Set some Global variables
  • Add submit listener to form
  • setCountDown() function
  • Add a click listener to stop-btn
  • Add a click listener to reset-btn
  • resetCountDown() function

Make sure you type all of the following between the proper comments.

Grab everything needed

We need a reference for elements to do something on it, right?. That's what we are going to do now.

We are going to grab the reference for the following using querySelector():

  • .form
  • .time-input
  • select[name='format']
  • .set-btn
  • .countdown
  • .stop-btn
  • .reset-btn

Type the following inside these comments /* grab necessary elements */ /* grab necessary elements ends */ :

// grab the .form
const form = document.querySelector('.form');
// grab the .time-input
const timeInput = document.querySelector('.time-input');
// grab the select[name='format']
const format = document.querySelector("select[name='format']");
// grab the .set-btn
const setBtn = document.querySelector('.set-btn');
// grab the .countdown
const countDown = document.querySelector('.countdown');
// grab the .stop-btn 
const stopBtn = document.querySelector('.stop-btn');
// grab the .reset-btn
const resetBtn = document.querySelector('.reset-btn');

Set some Global variables

Next, we need to define some variables in the global scope.

Type this between /* global variables and constants*/ /* global variables ends */ :

// variable to store setInterval
let countDownInterval;

// secondsLeft in millisecond
let secondsLeftms;
// end time
let endTime;
// .stop-btn clicked or not
let stopBtnClicked = false;

Add submit listener to form

Now, the next thing to do is that when a user types the time inside the input box and clicks the submit button, we need to grab the value and set the countdown. We are going to depend on milliseconds. So we had to convert any other format of time into milliseconds.

Type this between /* .form submit listener */ /* .form submit listener ends */ :

form.addEventListener('submit', (event) => {
  // prevent the default page reloading
  event.preventDefault();

  // get the countdown time user typed
  let countDownTime = timeInput.value;

  // check if it is not zero
  if (countDownTime > 0) {
    // check which is the format, ie the <select> element's value
    if (format.value === 'hours') {
      // convert hours to milliseconds
      // 1 hrs = 3600000 ms (5 zeros)
      countDownTime = countDownTime * 3600000;
    } else if (format.value === 'minutes') {
      // 1 minute = 60000 ms (4 zeros)
      countDownTime = countDownTime * 60000;
    } else if (format.value === 'seconds') {
      // 1 seconds = 1000 ms (3 zeros)
      countDownTime = countDownTime * 1000;
    }

    // get current time in milliseconds
    const now = Date.now();
    // calculate the ending time
    endTime = now + countDownTime;

    // activate the countdown at first
    setCountDown(endTime);

    countDownInterval = setInterval(() => {
      setCountDown(endTime);
    }, 1000);

    // then disable the .set-btn
    setBtn.disabled = true;
    // then enable the .stop-btn
    stopBtn.disabled = false;
  }

});

We are not going to depend entirely upon the setInterval() function. Rather we are calling another function inside setInterval() called setCountDown(). We hadn't created it yet. That's what we are going to do next. But what this setCountDown() function does is that it takes the endTime as an argument and sets the countdown timer more robustly.

setCountDown() function

Type this between /* setCountDown function */ /* setCountDown function ends */ :

const setCountDown = (endTime) => {
  // calculate how many milliseconds is left to reach endTime from now
  secondsLeftms = endTime - Date.now();
  // convert it to seconds
  const secondsLeft = Math.round(secondsLeftms / 1000);

  // calculate the hours, minutes and seconds
  let hours = Math.floor(secondsLeft / 3600);
  let minutes = Math.floor(secondsLeft / 60) - (hours * 60);
  let seconds = secondsLeft % 60;

  // adding an extra zero infront of digits if it is < 10
  if (hours < 10) {
    hours = `0${hours}`;
  }
  if (minutes < 10) {
    minutes = `0${minutes}`;
  }
  if (seconds < 10) {
    seconds = `0${seconds}`;
  }

  // stopping the timer if the time is up 
  if (secondsLeft < 0) {
    // resetCountDown();
    return;
  }

  // set the .countdown text
  countDown.innerHTML = `${hours} : ${minutes} : ${seconds}`;

};

Inside this function, we commented out the resetCountDown() function call like this:

commented out resetCountDown() function call

That's because we hadn't created it yet. And we will do it at last. So for now keep it as it is.

Let's add some functionality to the .stop-btn.

Add a click listener to stop-btn

Type this between /* .stop-btn click listener */ /* .stop-btn click listener ends */ :

stopBtn.addEventListener('click', () => {
  // toggle the value of 'stopBtnClicked'
  stopBtnClicked = !stopBtnClicked;

  // if STOP button is clicked
  if (stopBtnClicked === true) {
    // change the text to 'PLAY'
    stopBtn.innerHTML = 'PLAY';
    // enable the .reset-btn
    resetBtn.disabled = false;
    // clear the setInterval() inorder to freeze the countdown timer
    clearInterval(countDownInterval);
  } else if (stopBtnClicked === false) {
    // if PLAY button is clicked
    // then change text to 'STOP'
    stopBtn.innerHTML = 'STOP';
    // disable the .reset-btn
    resetBtn.disabled = true;
    // then update endTime
    endTime = secondsLeftms + Date.now();
    // set a new setInterval()
    countDownInterval = setInterval(() => {
      setCountDown(endTime);
    }, 1000);
  }
});

This will get activated whenever we click on the .stop-btn. This code is responsible for changing the STOP button to the PLAY button and vice versa.

Also, this code will pause the timer.

When you pause the timer, the endTime will not be the same as before, right? That's what we are doing in the last few lines.

Now let's add some functionality to the .reset-btn

Add a click listener to reset-btn

Type this between /* .reset-btn click listener */ /* .reset-btn click listener ends */

resetBtn.addEventListener('click', () => {
  resetCountDown();
});

When we click on the .reset-btn, it activates another function called resetCountDown().

Now, as the final step, create the resetCountDown() function, which will reset everything to its initial state.

resetCountDown() function

Type this between /* resetCountDown function */ /* resetCountDown function ends */ :

const resetCountDown = () => {
  // destroy the setInterval()
  clearInterval(countDownInterval);
  // reset the countdown text
  countDown.innerHTML = '00 : 00 : 00';
  // set stopBtnClicked = false
  stopBtnClicked = false;
  // change inner text to STOP
  stopBtn.innerHTML = 'STOP';

  // enable .set-btn
  setBtn.disabled = false;

  // disable .stop-btn and .reset-btn
  stopBtn.disabled = true;
  resetBtn.disabled = true;
};

Now let's uncomment the resetCountDown() function call inside setCountDown() like this:

previously commented out resetCountDown() function call

Uncommented resetCountDown() function call inside setCountDown() function

Hooray, we had reached the end :). Here is the final code for the "script.js" file. Make sure the code you typed up to this point inside the "script.js" is exactly like the following:

/* grab necessary elements */
// grab the .form
const form = document.querySelector('.form');
// grab the .time-input
const timeInput = document.querySelector('.time-input');
// grab the select[name='format']
const format = document.querySelector("select[name='format']");
// grab the .set-btn
const setBtn = document.querySelector('.set-btn');
// grab the .countdown
const countDown = document.querySelector('.countdown');
// grab the .stop-btn 
const stopBtn = document.querySelector('.stop-btn');
// grab the .reset-btn
const resetBtn = document.querySelector('.reset-btn');
/* grab necessary elements ends */ 


/* global variables and constants*/
// variable to store setInterval
let countDownInterval;

// secondsLeft in millisecond
let secondsLeftms;
// end time
let endTime;
// .stop-btn clicked or not
let stopBtnClicked = false;
/* global variables ends */


/* .stop-btn click listener */
stopBtn.addEventListener('click', () => {
  // toggle the value of 'stopBtnClicked'
  stopBtnClicked = !stopBtnClicked;

  // if STOP button is clicked
  if (stopBtnClicked === true) {
    // change the text to 'PLAY'
    stopBtn.innerHTML = 'PLAY';
    // enable the .reset-btn
    resetBtn.disabled = false;
    // clear the setInterval() inorder to freeze the countdown timer
    clearInterval(countDownInterval);
  } else if (stopBtnClicked === false) {
    // if PLAY button is clicked
    // then change text to 'STOP'
    stopBtn.innerHTML = 'STOP';
    // disable the .reset-btn
    resetBtn.disabled = true;
    // then update endTime
    endTime = secondsLeftms + Date.now();
    // set a new setInterval()
    countDownInterval = setInterval(() => {
      setCountDown(endTime);
    }, 1000);
  }
});
/* .stop-btn click listener ends */


/* .reset-btn click listener */
resetBtn.addEventListener('click', () => {
  resetCountDown();
});
/* .reset-btn click listener ends */


/* .form submit listener */
form.addEventListener('submit', (event) => {
  // prevent the default page reloading
  event.preventDefault();

  // get the countdown time user typed
  let countDownTime = timeInput.value;

  // check if it is not zero
  if (countDownTime > 0) {
    // check which is the format, ie the <select> element's value
    if (format.value === 'hours') {
      // convert hours to milliseconds
      // 1 hrs = 3600000 ms (5 zeros)
      countDownTime = countDownTime * 3600000;
    } else if (format.value === 'minutes') {
      // 1 minute = 60000 ms (4 zeros)
      countDownTime = countDownTime * 60000;
    } else if (format.value === 'seconds') {
      // 1 seconds = 1000 ms (3 zeros)
      countDownTime = countDownTime * 1000;
    }

    // get current time in milliseconds
    const now = Date.now();
    // calculate the ending time
    endTime = now + countDownTime;

    // activate the countdown at first
    setCountDown(endTime);

    countDownInterval = setInterval(() => {
      setCountDown(endTime);
    }, 1000);

    // then disable the .set-btn
    setBtn.disabled = true;
    // then enable the .stop-btn
    stopBtn.disabled = false;
  }

});
/* .form submit listener ends */


/* setCountDown function */
const setCountDown = (endTime) => {
  // calculate how many milliseconds is left to reach endTime from now
  secondsLeftms = endTime - Date.now();
  // convert it to seconds
  const secondsLeft = Math.round(secondsLeftms / 1000);

  // calculate the hours, minutes and seconds
  let hours = Math.floor(secondsLeft / 3600);
  let minutes = Math.floor(secondsLeft / 60) - (hours * 60);
  let seconds = secondsLeft % 60;

  // adding an extra zero infront of digits if it is < 10
  if (hours < 10) {
    hours = `0${hours}`;
  }
  if (minutes < 10) {
    minutes = `0${minutes}`;
  }
  if (seconds < 10) {
    seconds = `0${seconds}`;
  }

  // stopping the timer if the time is up 
  if (secondsLeft < 0) {
    resetCountDown();
    return;
  }

  // set the .countdown text
  countDown.innerHTML = `${hours} : ${minutes} : ${seconds}`;

};
/* setCountDown function ends */


/* resetCountDown function */
const resetCountDown = () => {
  // destroy the setInterval()
  clearInterval(countDownInterval);
  // reset the countdown text
  countDown.innerHTML = '00 : 00 : 00';
  // set stopBtnClicked = false
  stopBtnClicked = false;
  // change inner text to STOP
  stopBtn.innerHTML = 'STOP';

  // enable .set-btn
  setBtn.disabled = false;

  // disable .stop-btn and .reset-btn
  stopBtn.disabled = true;
  resetBtn.disabled = true;
};
/* resetCountDown function ends */

Wrapping Up

I hope you enjoyed this tutorial. If you had any doubts, then please comment them below. Thank you ;)