2

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();
Cray
  • 3,872
  • 7
  • 36
  • 96

1 Answers1

2

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;
  }
});
Danmoreng
  • 2,297
  • 1
  • 14
  • 28
  • OK, thanks. Now it works but only if the counter is in the visible area on page load. When I scroll to the counter, it stays at 0 – Cray Feb 22 '19 at 15:29
  • Your comment describes the first line of your requirement (in the question) - you need to check on the window/div scroll event (debounce it and check if already started) - rather than just once at startup – freedomn-m Feb 22 '19 at 15:30
  • @Cray Because it is only executed once at page load. I edited the answer with a reference to the scroll event listener. – Danmoreng Feb 22 '19 at 15:32
  • I thought I'm using something like that. See my edit in my question above. This works if there is only one counter. – Cray Feb 22 '19 at 15:32
  • Your util just checks if the element is in the view at the time of execution. it does not permanently check for it. I'll add the event listener code in the answer. – Danmoreng Feb 22 '19 at 15:34
  • Thanks! The addEventListener only once or for every instance? – Cray Feb 22 '19 at 15:42
  • Hmm, I try the code but it seems, that there are some `}`missing. Also it says `Uncaught ReferenceError: ticking is not defined` – Cray Feb 22 '19 at 15:47
  • Yes I just wrote what is necassary from the outside. Of course you need to put in your whole coding inside the checkForVisibility function. ticking is not defined is correct, that was mybad - it has to be defined before the event listener. I edited the answer. – Danmoreng Feb 22 '19 at 16:02
  • No it works! There is just one small issue... The counting stops if I scroll. Is there any chance to start the counting when the number hits the visible area? It should count up even if I scroll or leave the area – Cray Feb 22 '19 at 17:05