JavaScript Count-up Animation... 100

A JavaScript function to display incrementing numbers.

animateCountUp(element, duration, stepSize, startingValue)

Usage

First, import the module:

HTML
<script type="module">
        import animateCountUp from "./dist/index.js";
</script>

Now, write some HTML. For this example, we will use a container <div> and run the animation on each number within that container that has the .countup class. Note that you can have other elements with other classnames that won't interfere with the functionality, so we have added additional <span> tags that containe .before-number and .after-number classes. This is to show that we can have other characters such as dollar signs or any other type of text that you don't want to count up:

HTML

            <div id="container">
              <ul>
                <li>
                  <span class="before-number">$</span>
                  <span class="countup">45.23</span>
                  <span class="after-number">+</span>
                </li>
                <li>
                  <span class="countup">91.123</span>
                  <span class="after-number">%</span>
                </li>
                <li>
                  <span class="before-number ">₣</span>
                  <span class="number countup">42</span>
                  <span class="after-number">!</span>
                </li>
              </ul>
            </div>
            

Let's trigger this animation onClick() using a button:

HTML

          <button id="animateButton">
            Animate
          </button>
          

Now add JavaScript, using the animateCountUp() function along with the button as the event listener. We will also use a delay effect with setTimeout() to count each number at staggered times, each starting 700ms after the previous one, in this case. We are also going to add a callback function to animateCountUp() in order to disable the button from being clicked again until all of the animations are complete.

JavaScript

              document
                .getElementById("animateButton")
                .addEventListener("click", () => {
                  animateButton.disabled = true;
                  const container = document.querySelector("#container");
                  const countupEls = container.querySelectorAll(".countup");
                  const totalElements = countupEls.length;
                  let completedAnimations = 0;
              
                  countupEls.forEach((el, index) => {
                    setTimeout(() => {
                      animateCountUp(el, 7000, 7, null, () => {
                        completedAnimations++;
              
                        // Check if all animations are completed
                        if (completedAnimations === totalElements) {
                          console.log("Animation finished!");
                          animateButton.disabled = false;
                        }
                      });
                    }, index * 700);
                  });
                });
              

In this example, we search the #container <div> for any element that has the .countup class name. Then we apply the animateCountUp() function with a total duration of 7000ms, a step size value of 7 (only applicable to integers), and a default starting value of 0 (by setting null). The button is disabled until the animations are complete in order to prevent the user from triggering the animation again once it has been started.

  • $45.23+
  • 91.123%
  • 42!

Here is another example:

$200+
18.76%
😎185.32!

This is very similar to the first example, with different arguments passed into animateCountUp(), and starting each animation 500ms after the previous one by updating the setTimeout() function:

HTML

            <div id="container-two" class="example-two">
              <div class="card">
                <span class="before-number">$</span>
                <span id="countupTwo" class="countup">200</span>
                <span class="after-number">+</span>
              </div>
              <div class="card">
                <span id="countupThree" class="countup">18.76</span>
                <span class="after-number">%</span>
              </div>
              <div class="card">
                <span class="before-number">😎</span>
                <span id="countupFour" class="countup">185.32</span>
                <span class="after-number">!</span>
              </div>
            </div>
            
            <button id="animateButtonTwo">Animate</button>
            
JavaScript

          document
            .getElementById("animateButtonTwo")
            .addEventListener("click", () => {
              animateButtonTwo.disabled = true;
              const container = document.querySelector("#container-two");
              const countupEls = container.querySelectorAll(".countup");
              const totalElements = countupEls.length;
              let completedAnimations = 0;
          
              countupEls.forEach((el, index) => {
                setTimeout(() => {
                  animateCountUp(el, 3500, 1, 0, () => {
                    completedAnimations++;
                    if (completedAnimations === totalElements) {
                      animateButtonTwo.disabled = false;
                    }
                  });
                }, index * 500);
              });
            });
          

On Scroll

This function can also be triggered on scroll using JavaScript's Intersection Observer API. Keep scrolling past the code example to trigger the animation once the elements enter the viewport:

HTML

  <div id="container-four">
    <h2>This example is triggered on scroll</h2>
    <ul>
      <li>
        <span class="before-number">$</span>
        <span id="countupTwo" class="countup">200</span>
        <span class="after-number">+</span>
      </li>
      <li>
        <span id="countupThree" class="countup">304.7</span>
        <span class="after-number">%</span>
      </li>
      <li>
        <span class="before-number">+</span>
        <span id="countupFour" class="countup">430.32</span>
        <span class="after-number">!</span>
      </li>
    </ul>
  </div>
  
JavaScript

  const containerObserver = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        const countupEls = entry.target.querySelectorAll(".countup");
        countupEls.forEach((el, index) => {
          setTimeout(() => {
            animateCountUp(el, 4000, 1, 0);
          }, index * 700);
        });
  
        containerThreeObserver.unobserve(entry.target);
      }
    });
  });
  
  // Observe the container div
  containerObserver.observe(
    document.getElementById("container")
  );
  

This example is triggered on scroll

  • $200+
  • 304.7%
  • +430.32!