One of the standard tools you need to know how to do in JavaScript is...how to manipulate time. To demonstrate this, we will build a countdown timer using vanilla JavaScript and CSS.
This is a simple project, but one that shows you how to manipulate time in JS, customize attributes in CSS, and finally add custom parameters to your JS class.
View This On YouTube
File Structure
index.html
/sass
style.scss
/js
init.js
countdown.js
/css (generated by Sass)
style.css
style.min.css
Our HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Countdown Clocks</title>
<link rel="stylesheet" href="/css/style.min.css">
</head>
<body>
<h2>Countdown Clock 1</h2>
<div class="clock" id="clock1">
<div data-value="days"></div>
<div data-value="hours"></div>
<div data-value="minutes"></div>
<div data-value="seconds"></div>
</div>
<script src="/js/countdown.js"></script>
<script src="/js/init.js"></script>
</body>
</html>
Now that we have the basic HTML structure down, we can create the JS class that adds data. Notice we are using data-value
so we don't need to add a string in the HTML there, we will modify that will CSS.
Our Countdown JS Class
// class to create a countdown timer
class CountdownTimer {
// setup timer values
constructor({ selector, targetDate, backgroundColor = null, foregroundColor = null }) {
this.selector = selector;
this.targetDate = targetDate;
this.backgroundColor = backgroundColor;
this.foregroundColor = foregroundColor;
// grab divs on frontend using supplied selector ID
this.refs = {
days: document.querySelector(`${this.selector} [data-value="days"]`),
hours: document.querySelector(`${this.selector} [data-value="hours"]`),
mins: document.querySelector(`${this.selector} [data-value="minutes"]`),
secs: document.querySelector(`${this.selector} [data-value="seconds"]`),
};
}
getTimeRemaining(endtime) {
const total = Date.parse(endtime) - Date.parse(new Date());
const days = Math.floor(total / (1000 * 60 * 60 * 24));
const hours = Math.floor((total / (1000 * 60 * 60)) % 24);
const mins = Math.floor((total / 1000 / 60) % 60);
const secs = Math.floor((total / 1000) % 60);
return {
total,
days,
hours,
mins,
secs,
};
}
updateTimer({ days, hours, mins, secs }) {
this.refs.days.textContent = days;
this.refs.hours.textContent = hours;
this.refs.mins.textContent = mins;
this.refs.secs.textContent = secs;
}
updateColors() {
if (this.backgroundColor != null) {
this.refs.days.style.background = this.backgroundColor;
this.refs.hours.style.background = this.backgroundColor;
this.refs.mins.style.background = this.backgroundColor;
this.refs.secs.style.background = this.backgroundColor;
}
if (this.foregroundColor != null) {
this.refs.days.style.color = this.foregroundColor;
this.refs.hours.style.color = this.foregroundColor;
this.refs.mins.style.color = this.foregroundColor;
this.refs.secs.style.color = this.foregroundColor;
}
}
startTimer() {
const timer = this.getTimeRemaining(this.targetDate);
this.updateTimer(timer);
this.updateColors();
setInterval(() => {
const timer = this.getTimeRemaining(this.targetDate);
this.updateTimer(timer);
}, 1000);
}
}
This class will control the div
and data-values
we need to replace. We create this as a class so we can simply add a few lines of code to add the countdown on any page through our init.js
file.
This class takes the selector
, the targetDate
, and optionally a backgroundColor
and foregroundColor
. We will pass these values when we call the class.
Calling Our Class
In your init.js
file, you can call the countdown class using something like this:
const timer = new CountdownTimer({
selector: "#clock1",
targetDate: new Date("September, 21 2022 18:00:00"),
});
That code will initialize our class and start a countdown to the date we provided. You can also add a background and foreground color to make it a little more custom. They are not required but it would look something like this:
const timer = new CountdownTimer({
selector: "#clock1",
targetDate: new Date("September, 21 2022 18:00:00"),
backgroundColor: "rgba(0,0,0,.15)",
foregroundColor: "rgba(0,0,0,.50)",
});
timer.startTimer();
You must use both background and foreground colors if you customize one or the other. We have it set up for a null value, but in the end, if you use one, you must use the other.
It is that easy. Now that you have your class set up, you can add your countdown to any div you want. You can also add multiple countdowns on one page. For example:
<h2>Countdown Clock 1</h2>
<div class="clock" id="clock1">
<div data-value="days"></div>
<div data-value="hours"></div>
<div data-value="minutes"></div>
<div data-value="seconds"></div>
</div>
<h2>Countdown Clock 2</h2>
<div class="clock" id="clock2">
<div data-value="days"></div>
<div data-value="hours"></div>
<div data-value="minutes"></div>
<div data-value="seconds"></div>
</div>
// setup timer with set textual date in the future
const timer1 = new CountdownTimer({
selector: "#clock1",
targetDate: new Date("September, 21 2022 18:00:00"),
});
timer1.startTimer();
// setup timer with date set in the future
const timer2 = new CountdownTimer({
selector: "#clock2",
targetDate: new Date("September, 21 2022 18:00:00"),
backgroundColor: "rgba(0,0,0,.15)",
foregroundColor: "rgba(0,0,0,.50)",
});
timer2.startTimer();
Note In order to use this class on multiple divs, you must pass a unique selector
that matches the div ID. In this case, we are using #clock1
and #clock2
that matches our div IDs.
Our Countdown Styles
This part is pretty standard if you have ever used CSS or Sass. Note in the Sass, we are customizing the countdown divs using their data-value
attribute. This is where we add the text for our div (Days, Hours, Mins, Secs).
body {
background-color: #38a4ef;
color:#fff;
font-size:16px;
text-align:center;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}
.clock {
display:block;
margin: 0 auto;
max-width:80%;
div {
background-color:rgba(255,255,255,.25);
color:#fff;
display:inline-block;
padding:2rem;
margin: 0 1rem;
font-size:2.5rem;
width: calc(10% - 2rem);
text-align:center;
font-weight:bold;
border-radius:5%;
&[data-value*="days"]:after, &[data-value*="hours"]:after, &[data-value*="minutes"]:after, &[data-value*="seconds"]:after {
display:block;
font-size:.75rem;
margin-top:.25rem;
font-weight: 300;
}
&[data-value*="days"]:after {
content:"Days"
}
&[data-value*="hours"]:after {
content:"Hours"
}
&[data-value*="minutes"]:after {
content:"Minutes"
}
&[data-value*="seconds"]:after {
content:"Seconds"
}
}
}
@media screen and (max-width: 820px) {
.clock {
max-width:90%;
div {
width:calc(15% - 2rem)
}
}
}
@media screen and (max-width: 767px) {
.clock {
max-width:100%;
div {
width:calc(30% - 4rem);
margin:.5rem;
padding: .5rem;
font-size:1rem;
&[data-value*="days"]:after, &[data-value*="hours"]:after, &[data-value*="minutes"]:after, &[data-value*="seconds"]:after {
font-size:.5rem;
}
}
}
}
We are simply using the :after CSS selector to add the words to the div. This prevents us from having to edit it each time we place it on a page.
I have also added the responsive media queries to our Sass stylesheet so it should work on tablets and mobile phones.
Conclusion
There you have it, a vanilla JavaScript countdown timer. It is a simple project, yet goes over some pretty cool things you can do with JS and CSS. I hope it helps in your future projects.
Read more articles on DevDrawer
Top comments (3)
You need a
and a
somewhere in your code :-)
Otherwise it will never start. Also idea to do something if timer is passed
Also, I am about to do a tutorial on an alarm system using the same type of code that does an alarm sound once it reaches the correct time, you could use that code and add it to this to make it do something is the time has passed. Take a look at it when I push it out in a few days.
You are correct. I don't know why but I forgot that part. I will add it to the script.