Search code examples
javascripthtmlcounter

Javascript counter for big and small numbers


I have been working on a counter for a webpage and I had been stuck with this function that I can't solve.

I have a counter for 4 divs and since two of them are small numbers and the other two are big numbers the first ones run soo fast that I can't see them to the function.

Someone knows how to set the js function so that they can run at a different speed, and finish at the same time?

let section_counter = document.querySelector('#section_counter');
let counters = document.querySelectorAll('.counter-item .counter');

// Scroll Animation

let CounterObserver = new IntersectionObserver(
  (entries, observer) => {
    let [entry] = entries;
    if (!entry.isIntersecting) return;

    let speed = 1000;
    counters.forEach((counter, index) => {
      function UpdateCounter() {
        const targetNumber = +counter.dataset.target;
        const initialNumber = +counter.innerText;
        const incPerCount = targetNumber / speed;
        if (initialNumber < targetNumber) {
          counter.innerText = Math.ceil(initialNumber + incPerCount);
          setTimeout(UpdateCounter, 50);
        }
      }
        
      UpdateCounter();

      if (counter.parentElement.style.animation) {
        counter.parentElement.style.animation = '';
      } else {
        counter.parentElement.style.animation = `slide-up 0.3s ease forwards ${
          index / counters.length + 0.5
        }s`;
      }
    
    });
    observer.unobserve(section_counter);
  },
  {
    root: null,
    threshold: window.innerWidth > 768 ? 0.4 : 0.3,
  }
);

CounterObserver.observe(section_counter);
    <section id="section_counter">
      <div class="container">
        <h1 class="section-heading">For every set, you purchase of our sustainable wear, you are helping the world save: </h1>
      </div>
      <div class="container">
        <div class="counter-grid">
            
          <div class="counter-item">
            <h1 class="counter" data-target="15">0</h1>
            <h2 class="counter-heading">Plastic bottles</h2>
            <div class="counter-text">Which takes up to 150 years to decompose</div>
          </div>
          <div class="counter-item">
            <h1 class="counter" data-target="22">0</h1>
            <h2 class="counter-heading">KW of energy</h2>
            <div class="counter-text">Equivalent to 1-day household consumption</div>
          </div>
          <div class="counter-item">
            <h1 class="counter" data-target="5900">0</h1>
            <h2 class="counter-heading">Liters of water</h2>
            <div class="counter-text">Equivalent to the daily consumption of 16 persons (Mexico average)</div>

          </div>
          <div class="counter-item">
          <h1 class="counter" data-target="9000">0</h1>
          <h2 class="counter-heading">Grams of Co2 emissions</h2>
          <div class="counter-text">Equivalent to a 90 km car consumption</div>

          </div>
        </div>
      </div>
    </section>


Solution

  • Try this solution with using additional attribute data-count. The logic is a simple: we store the calculation with the decimal value in data-count and put only an integer inside, remove data-count at the end.

    let section_counter = document.querySelector('#section_counter');
    let counters = document.querySelectorAll('.counter-item .counter');
    
    // Scroll Animation
    
    let CounterObserver = new IntersectionObserver(
      (entries, observer) => {
        let [entry] = entries;
        if (!entry.isIntersecting) return;
    
        let speed = 1000;
        counters.forEach((counter, index) => {
          const targetNumber = +counter.dataset.target;
          // Find the unit for one counter step
          const count = targetNumber / speed;
          // Set data attribute for accurate calculation with decimal
          counter.setAttribute('data-count', 0);
    
          function UpdateCounter() {
            const initialNumber = +counter.innerText;
            // Get decimal number to calculate
            const countNumber = +counter.dataset.count;
            // Getting an integer value with Math.floor()
            const integer = Math.floor(countNumber);
            if (initialNumber < targetNumber) {
              // Set decimal number / toFixed() with speed length, to increase accuracy
              counter.setAttribute('data-count', (countNumber + count).toFixed(`${speed}`.length));
              // Set integer number
              counter.innerText = integer;
              setTimeout(UpdateCounter, 50);
            } else {
              // remove additional data attribute
              counter.removeAttribute('data-count');
            }
          }
    
          UpdateCounter();
    
          if (counter.parentElement.style.animation) {
            counter.parentElement.style.animation = '';
          } else {
            counter.parentElement.style.animation = `slide-up 0.3s ease forwards ${
                index / counters.length + 0.5
              }s`;
          }
        });
        observer.unobserve(section_counter);
      }, {
        root: null,
        threshold: window.innerWidth > 768 ? 0.4 : 0.3,
      }
    );
    
    CounterObserver.observe(section_counter);
    <section id="section_counter">
      <div class="container">
        <h1 class="section-heading">
          For every set, you purchase of our sustainable wear, you are helping the world save:
        </h1>
      </div>
      <div class="container">
        <div class="counter-grid">
          <div class="counter-item">
            <h1 class="counter" data-target="15">0</h1>
            <h2 class="counter-heading">Plastic bottles</h2>
            <div class="counter-text">Which takes up to 150 years to decompose</div>
          </div>
          <div class="counter-item">
            <h1 class="counter" data-target="22">0</h1>
            <h2 class="counter-heading">KW of energy</h2>
            <div class="counter-text">Equivalent to 1-day household consumption</div>
          </div>
          <div class="counter-item">
            <h1 class="counter" data-target="5900">0</h1>
            <h2 class="counter-heading">Liters of water</h2>
            <div class="counter-text">
              Equivalent to the daily consumption of 16 persons (Mexico average)
            </div>
          </div>
          <div class="counter-item">
            <h1 class="counter" data-target="9000">0</h1>
            <h2 class="counter-heading">Grams of Co2 emissions</h2>
            <div class="counter-text">Equivalent to a 90 km car consumption</div>
          </div>
        </div>
      </div>
    </section>