Search code examples
javascriptjquerycounterviewport

Multiple viewport checks per page


I'm using a script to count numbers from 0 to their actual value. To start the counter, I want the element to be in the visible area of the viewport.

I found a solution to check if an element is in the visible area. But that doesn't work if I'm using more than one number elemt on different areas of the page. It counts every element with the same class (.counter).

If I use the counter script twice with a different name, the second version doesn't work and the first one doesn't work on scroll. Only if I have the counter in the visible area on pageload.

Here's my counter code:

$('.counter').each(function() {
    var $this = $(this),
    countTo = $this.attr('data-count');

    $({ countNum: $this.text()}).animate({
        countNum: countTo
    },

    {
        duration: 2000,
        easing:'linear',
        step: function() {
            $this.text(Math.floor(this.countNum));
        },
        complete: function() {
            $this.text(this.countNum);
        }

    });

});

And this is the solution I tried (and it works once per page): https://stackoverflow.com/a/488073/1788961

Here you find a fiddle with the whole code: https://codepen.io/cray_code/pen/QYXVWL

This is the code to check if I scroll to the element (see linked answer above):

function isScrolledIntoView(elem)
{
    var docViewTop = $(window).scrollTop();
    var docViewBottom = docViewTop + $(window).height();

    var elemTop = $(elem).offset().top;
    var elemBottom = elemTop + $(elem).height();

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}


function Utils() {

}

Utils.prototype = {
    constructor: Utils,
    isElementInView: function (element, fullyInView) {
        var pageTop = $(window).scrollTop();
        var pageBottom = pageTop + $(window).height();
        var elementTop = $(element).offset().top;
        var elementBottom = elementTop + $(element).height();

        if (fullyInView === true) {
            return ((pageTop < elementTop) && (pageBottom > elementBottom));
        } else {
            return ((elementTop <= pageBottom) && (elementBottom >= pageTop));
        }
    }
};

var Utils = new Utils();

Solution

  • You check isElementInView once instead of for each element individually. Of course it starts all counters.

    var isElementInView = Utils.isElementInView($('.counter'), false);
    if (isElementInView) {
                $('.counter').each(function() {
    

    Move it inside your .each function and it will work for each counter separately.

    $('.counter').each(function() {
                    var $this = $(this),
                    countTo = $this.attr('data-count');
                    var isElementInView = Utils.isElementInView($this, false);
                    if (isElementInView) {
                    // do animation
    

    If you want that to happen when you scroll you need to add an EventListener to the page that executes the code everytime you scroll: https://developer.mozilla.org/en-US/docs/Web/Events/scroll

    function checkForVisible(){
        $('.counter').each(function() {
                    var $this = $(this),
                    countTo = $this.attr('data-count');
                    var isElementInView = Utils.isElementInView($this, false);
                    if (isElementInView) {
                    // etc code
    }
    
    var ticking = false;
    window.addEventListener('scroll', function(e) {
      if (!ticking) {
        window.requestAnimationFrame(function() {
          checkForVisible();
          ticking = false;
        });
        ticking = true;
      }
    });