Create a pure Javascript Scroll Counter

Javascript Counter in action

I really don’t know why my clients love to have animated counters in their pages. But the fact is that they do. And using a jQuery plugin or a custom library just for that is an over kill.

So this is an small counter that starts its count when the element with an special counter class gets visible on a page.

Take into account that if you want to change its behavior, you have to tweak the code since I’m not interested in adding configuration options to it.

The code

So here is the code.

"use strict"

document.addEventListener("DOMContentLoaded", function() {
  // You can change this class to specify which elements are going to behave as counters.
  var elements = document.querySelectorAll(".scroll-counter")

  elements.forEach(function(item) {
    // Add new attributes to the elements with the '.scroll-counter' HTML class
    item.counterAlreadyFired = false
    item.counterSpeed = item.getAttribute("data-counter-time") / 45
    item.counterTarget = +item.innerText
    item.counterCount = 0
    item.counterStep = item.counterTarget / item.counterSpeed

    item.updateCounter = function() {
      item.counterCount = item.counterCount + item.counterStep
      item.innerText = Math.ceil(item.counterCount)

      if (item.counterCount < item.counterTarget) {
        setTimeout(item.updateCounter, item.counterSpeed)
      } else {
        item.innerText = item.counterTarget
      }
    }
  })

  // Function to determine if an element is visible in the web page
  var isElementVisible = function isElementVisible(el) {
    var scroll = window.scrollY || window.pageYOffset
    var boundsTop = el.getBoundingClientRect().top + scroll
    var viewport = {
      top: scroll,
      bottom: scroll + window.innerHeight,
    }
    var bounds = {
      top: boundsTop,
      bottom: boundsTop + el.clientHeight,
    }
    return (
      (bounds.bottom >= viewport.top && bounds.bottom <= viewport.bottom) ||
      (bounds.top <= viewport.bottom && bounds.top >= viewport.top)
    )
  }

  // Funciton that will get fired uppon scrolling
  var handleScroll = function handleScroll() {
    elements.forEach(function(item, id) {
      if (true === item.counterAlreadyFired) return
      if (!isElementVisible(item)) return
      item.updateCounter()
      item.counterAlreadyFired = true
    })
  }

  // Fire the function on scroll
  window.addEventListener("scroll", handleScroll)
})

As you can see is very short and in my opinion easy to understand. If you don’t follow, read along…

Explanation of the code

First of all, the code is enclosed in a DOMContentLoaded event listener. This is to make sure that we won’t start looking for counters until the page html content is loaded. This is the equivalent of $(document).ready() function in jQuery.

Next, we’ll parse the page, looking for the elements with the class .scroll-counter and in each found element will be doing the following:

After we do all that with each counter element, we create 2 additional functions in the page:

Finally we create a window event listener for the scroll event.

Usage

To make an element a counter you just need to add something like

<span class="scroll-counter" data-counter-time="1000">150</span>

And it will count from 0 to 150 in about a second.

Javascript Counter in action

Hope you found this short article useful ☺️