Search code examples
numberscounterintersection-observer

Using Intersection Observer to target the same class at different times throughout the page


I've got various animated counters throughout a page which need to count up to the specified number when they each become visible for the first time within the browser viewport.

The following code works but activates all of the counters at the same time (even when not visible).

I'm not sure what I'm doing wrong?

<script>
        jQuery(document).ready(function($){

            function startCounter(){
                $('.counter').each(function() {
                    var num = jQuery(this), countTo = num.attr("data-count");
                    var decimal = 0;
                    if (countTo.indexOf(".") > 0) { 
                        decimal = countTo.toString().split(".")[1].length; 
                    }
                    $({ countNum: num.text()}).animate({ countNum: countTo }, {
                        duration: 3000, easing:"swing", step: function() {
                            num.text(parseFloat(this.countNum).toFixed(decimal));
                            //num.text(Math.floor(this.countNum));
                        }, complete: function() {
                            num.text(this.countNum);
                        }
                    });
                });
            }   

            let counters = document.querySelectorAll('.counter');
            
            let observer = new IntersectionObserver(
              (entries, observer) => {
                entries.forEach((entry) => {
                  if (entry.isIntersecting) {
                    startCounter();
                    //observer.unobserve(entry.target);
                  }
                });
              },
              { threshold: 1 }
            );

            counters.forEach(counter => {
              observer.observe(counter);
            });
        });

    </script>
    <body>
        <div class="counter" data-count="1000.45"></div><div class="counter" data-count="2306.33"></div>
        <div style="height:100vh;"></div>
        <div class="counter" data-count="98"></div>
        <div class="counter" data-count="560"></div>
    </body>

Solution

  • You are targeting all the elements with the class 'counter' in startCounter function by using $('.counter').each(function().

    So for each entry in your observer, i.e if any of the entries become visible on DOM, all the elements (i.e counters) get affected.

    You only have to target the entry which is visible (i.e isIntersecting) on DOM, so it is suggested that you pass that particular element(entry.target) as an argument to startCounter, and only make the necessary changes on that particular element