Sometimes I think I'm a Stack Overflow junky 🤪... And my addiction was worse a few months ago when I saw this question and couldn't resist my fingers from tackling it.
The main idea was to..
🛠️ Build a JavaScript algorithm that would make counters start at different speeds depending on the value set in the speed
attribute of the HTML span
tag elements.
I feel that is an interesting and fun code to share with others.
If you want to try out the snippet 👉 click here
Before you continue..
I've written this post in great detail so that it can be understood by begginers. The idea of the post is to have fun programming, learn/practice a few things, experiment and maybe come up with a different or even a better solution that the one I came up with.
If(you_want_to_continue === true) console.log('keep scrolling..')
else console.log('see you! 👋')
Grab some ☕ and lets get started!
Go to your favorite JS code editor (JSFiddle is pretty cool) and setup the HTML
<h1 class="counter" speed='3'></h1>
<h1 class="counter" speed='7'></h1>
<h1 class="counter" speed='10'></h1>
<h1 class="counter" speed='12'></h1>
As you can see, I added the speed
attribute to the h1
elements. The class counter
is to let JS know which elements we want to select using the querySelectorAll()
Now that the HTML code is ready is time to start programming
Let's declare the elements
const
const elements = document.querySelectorAll('.counter')
💬 For those who don't know, the document method querySelectorAll()
returns a static NodeList representing a list of the document's elements that match the specified group of selectors.
🧐 So, in this case elements
will contain all h1
elements that have the class counter
.
console.log(elements)
outputs:
{
"0": <h3 class="counter" speed="6"></h3>,
"1": <h3 class="counter" speed="7"></h3>,
"2": <h3 class="counter" speed="8"></h3>,
"3": <h3 class="counter" speed="9"></h3>,
"item": function item() {
[native code]
},
"keys": function keys() {
[native code]
},
"values": function values() {
[native code]
},
"entries": function entries() {
[native code]
},
"forEach": function forEach() {
[native code]
},
"length": 4
}
This doesn't look very nice, I know.. But accessing the elements
is simple.
They can be accessed by their 🗝️ key: elements[key]
. For example elements['1']
returns the following HTML element:
<h3 class="counter" speed="7"></h3>
Now that we understand how to retrieve and work with the static NodeList elements
let's start to build the logic by creating a function that will set intervals for every HTML element in elements
.
function setCounter(element, speed, limit){
let count = 0 // every count will start from 0
let interval = setInterval(() => {
// in every interval the count will increment by the speed value
count += speed
// update the HTML in this specific element
element.innerHTML = count
// stop the setInterval() if condition is met
if(count >= limit) clearInterval(interval)
}, 1) // <--- the interval will loop every 1 millisecond
}
Now that the setCounter()
function is ready we need to loop through all the elements to retrieve their speed, and invoke the setCounters()
but before doing that, we need to be clear on how to get and use all speeds.
To retrieve the speed we need to specify from which element are we going to get the attribute from:
elements[key].getAttribute('speed')
💬 When using .getAttribute()
the returned value comes as a string. This can be prooven by using typeof
like so:
let key = '1'
let value = elements[key].getAttribute('speed')
console.log(typeof value) // --> string
To make the code work, we need to convert to integer by using Number()
or parseInt()
.
After this we have all needed to invoke setCounter(element, speed, limit)
. This will initialize each setInterval
with their respective parameters.
elements.forEach((el, key) => {
let speed = Number(elements[key].getAttribute('speed'))
setCounter(el, speed, 1000)
})
Now that the code is completed do you think is going to work properly ?
Let's give it a shot and see what happens. Take into consideration that the limit
passed as a parameter to setCounter()
is 1000
It didn't work properly 😭... The setIntervals
are working but they are showing a bigger count
than the limit
. This is because the value of the speed
that is added to the count is not summing 1
in every loop, it can be any number so it can overflow the limit
.
One way of solving this, is by using a ternary operator inside the setInterval()
, like so:
count = (count >= limit) ? limit : count + speed
How does this work?
If count
is bigger or equal to limit
, count
will equal to limit
and in the opposite case count
will equal count + speed
.
This is a minor patch to solve the last loop count problem. By doing this the count will never overflow the limit.
The resulting code ✔️
const elements = document.querySelectorAll('.counter')
elements.forEach((el, key) => {
let speed = Number(elements[key].getAttribute('speed'))
setCounter(el, speed, 1000)
})
function setCounter(element, speed, limit){
let count = 0
let interval = setInterval(() => {
count = (count >= limit) ? limit : count + speed
if(count === limit) clearInterval(interval)
element.innerHTML = count
}, 1)
}
This is my first post in dev.to
Feel free to experiment/play with the code and share your unique approach 👍 if you have one 🙃
Top comments (0)