Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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>
like image 678
Mario Sosa Avatar asked Feb 14 '26 10:02

Mario Sosa


1 Answers

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>
like image 102
Anton Avatar answered Feb 16 '26 00:02

Anton