I am sure at some point in your life while browsing the web, you have found a web page that shows an animated counter. A counter that starts from 0 and goes all the way up to some given number. Here is an example of how such a counter looks.
In this article, we are going to build exactly a counter like this.
Prerequisites:
- Basic HTML
- Basic CSS(including flexbox)
- Basic JavaScript
This article is going to be a shorter one. So, without wasting any more time, let’s get started.
The Markup (HTML)
The markup for this example has one container. All our elements are going to be inside a parent container. All the individual counter elements, including the icons, text, and time, will be under another container. Let’s see the markup.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loading Counter</title>
<link rel="stylesheet" href="styles.css">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;900&display=swap" rel="stylesheet">
<script src="script.js" defer></script>
</head>
<body>
<div class="container">
<div class="heading">
Counting Upto the Limit
</div>
<div class="counter-container">
<div class="counter">
<img src="./icons/iconmonstr-time-19.svg" alt="timer" srcset="" class="icon">
<h3 data-target="15000" class="count">0</h3>
<h6>Work Hours</h6>
</div>
<div class="counter">
<img src="./icons/iconmonstr-coffee-11.svg" alt="Coffee" srcset="" class="icon">
<h3 data-target="1200" class="count">0</h3>
<h6>Cups of Coffee</h6>
</div>
<div class="counter">
<img src="./icons/iconmonstr-weather-112.svg" alt="night" srcset="" class="icon">
<h3 data-target="500" class="count">0</h3>
<h6>Sleepless Nights</h6>
</div>
</div>
</div>
</body>
</html>
As you can see, at the top, we include our stylesheet file and the scripts file. The defer
tag in the script tag will ensure that the javascript loads after the HTML completes the loading. We are using the Nunito google font. All the icons in this tutorial are from iconmonstr. I have downloaded these three icons for the sake of this tutorial. Inside the container div
, we are adding a div
that will hold the heading. We are calling this div
heading
. Then, we are creating another div
called counter-container
, that will keep all our counters. Each counter has a counter div
. The counter div
holds icon, an H3
tag with 0 inside it, and an H6
tag with some text inside. Look closely at the H3
tag. This tag has a class of count
and an attribute called data-target
. The data-*
attribute allows us to store extra information. Any user-defined name that starts with data-
in front of it will be counted as a non-standard attribute.
These values can be used in JavaScript. Reading these values in JavaScript is also very simple. We can use the getAttribute
method to read them. You’ll see it when we access it in JavaScript. In our code, these data-target
attributes will say how much we want to count. For example, 15000
in the first attribute will count from 0 to 15000. Because our base value is zero, this is why we are adding 0 to the H3
s.
So, our markup is done. Now, let’s jump to the CSS part.
Styling the Counter (CSS)
For this example, we are going to use extremely simple styles. Just a few flexboxes to get the positions we want.
Like all the other tutorials, our first thing in styling this is to reset the default values and change the box-sizing
to border-box
.
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
Because we are going to use a google font Nunito, we have to define it in the body of our document. I am also going to add a background color.
body {
font-family: 'Nunito', sans-serif;
background: #fbf7f4;
}
The next thing is the container.
.container {
width: 80%;
margin: auto;
}
These styles will restrict the container to a width of 80% of the parent. The auto
margin will center the container. Now, let’s style the heading.
.heading {
text-align: center;
font-size: 3.5rem;
font-weight: bold;
padding: 5rem 0;
}
I don’t think that I have to explain anything here. The styles are pretty self-explanatory. Let’s move on to styling the counter-container
now.
.counter-container {
display: flex;
justify-content: space-around;
align-items: center;
}
We want all the counters to be centered. To do this, we will take the help of flexbox. If you don’t know about flexboxes, here is a two-part article for you,
Our counters are now centered. Let's center the texts inside the counters. We can use the text-align
property here. Each counter is inside the counter
div
. Let's target the div
and center the texts.
.counter {
text-align: center;
}
Now let's style the headings inside the counters. It is also very simple. I am going to paster the codes below.
.counter h3 {
padding: 0.5rem 0;
font-size: 2.5rem;
font-weight: 800;
}
.counter h6 {
font-size: 2rem;
padding-bottom: 1rem;
}
Finally, let's increase the icon size.
.icon {
height: 5rem;
width: auto;
}
We are extending the height only and setting the width to auto to adjust the width automatically.
And we are done with the CSS part.
The JavaScript
Inside our JavaScript, we only need to target a single DOM. If you go back to the HTML and check the counter H3
, you’ll see that we have a class inside the H3
tag. It is the count
tag and is available on every counter header. So, in JavaScript, to target this DOM we can use the querySelectorAll
property. This property will select all the count
classes. Another thing that we need in our JavaScript is the speed variable. A speed variable will be used to control the counter’s speed, i.e., how fast it counts.
const counters = document.querySelectorAll('.count');
const speed = 200;
Now, we will loop through each of the counters and execute a function. This function will do all the counting. Because we are going to run a function inside the loop, we can use the forEach
loop. We have to pass in at least one parameter in a forEach
loop. This element is the current value of the loop. Let’s say it counter
for our app.
Inside the forEach
loop, we will create a function that will do all the work. Let’s call this function updateCount
.
counters.forEach((counter) => {
const updateCount = () => {
const target = parseInt(counter.getAttribute('data-target'));
const count = parseInt(counter.innerText);
const increment = Math.trunc(target / speed);
};
updateCount();
});
Let’s first understand the code up to the above part. The target
variable will hold the target that is defined using the data-
attribute in our HTML. As you can see, to get the data values, we use the getAttribute
method. And inside the method, we pass in the attribute's complete name, including data-
tag. The parseInt
in front of it will convert the value into an integer because, by default, the return value is of string type. Then we take the value of the text that is inside our H3
. At the start, this value will be 0. So, why are we not using it directly? Because the way our counter works is that it adds a number to the inner text of the H3
until it reaches the target value. For this, we have to keep track of the value that is generated each time by adding the increment value. We are also converting it to an integer. We can also use a +
sign in place of the parseInt
. The increment variable holds the number that is to be added after a specified time. We calculate it by dividing the target by speed. The Math.trunc
will round off the value that we get by dividing. In JavaScript, there are multiple ways to round off a value. Here’s an article that talks about the different rounding methods.
The next thing is to check if we have reached the target value. And if the current value is less than the target value then we add up our increment value until we reach the target. This is a simple if-else
statement.
if (count < target) {
counter.innerText = count + increment;
setTimeout(updateCount, 1);
} else {
count.innerText = target;
}
First, we are adding our increment
value to the counter and changing the DOM value using counter.innerText
. We are passing two parameters inside the setTimeout
function. The setTimeout
function executes a specific function after a given period of time. In our case, the first parameter is the updateCount
function, and the 1 represents one millisecond. So, it will execute the updateCount
function after every one millisecond until the count value is less than the target value. Otherwise, we set the inner text to the target value. The only step that is left, is to call our updateCount
function. So, the complete JavaScript will look like this,
const counters = document.querySelectorAll('.count');
const speed = 200;
counters.forEach((counter) => {
const updateCount = () => {
const target = parseInt(counter.getAttribute('data-target'));
const count = parseInt(counter.innerText);
const increment = Math.trunc(target / speed);
if (count < target) {
counter.innerText = count + increment;
setTimeout(updateCount, 1);
} else {
counter.innerText = target;
}
};
updateCount();
});
And our counter is done. Here is a complete working version of our counter.
You can also find the complete code on this Github repo.
Conclusion
So, this was a pretty easy tutorial. It is a great one to practice your basic JavaScript knowledge. I hope you enjoyed building this one. Here are two more such projects that will help you to grow your JavaScript knowledge.
For more such tutorials and projects, keep visiting nemotivity.dev.