How can this counter start from zero?

Viewed 182

How can this script start counting from zero? At the moment it starts with the number it's supposed to count to before starting from zero

The JavaScript function loads the counter when it is called into view. How can the numerical values in the counter start with zeros when it is called into view

function isVisible(el) {
  const element = $(el);
  var WindowTop = $(window).scrollTop();
  var WindowBottom = WindowTop + $(window).height();
  var ElementTop = element.offset().top;
  //var ElementBottom = ElementTop + element.height();
  var ElementBottom = ElementTop + 20;
  return ElementBottom <= WindowBottom && ElementTop >= WindowTop;
}

function Counter(el) {
  obj = $(el);
  if (obj.hasClass("ms-animated")) {
    return;
  }
  obj.addClass("ms-animated");

  // get the number
  var number = obj.text();
  obj.attr("data-number", number);

  // clear the HTML element
  obj.empty();

  // create an array from the text, prepare to identify which characters in the string are numbers
  var numChars = number.split("");
  var numArray = [];
  var setOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  // for each number, create the animation elements
  for (var i = 0; i < numChars.length; i++) {
    if ($.inArray(parseInt(numChars[i], 10), setOfNumbers) != -1) {
      obj.append(
        '<span class="digit-con"><span class="digit' +
        numArray.length +
        '">0<br>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br></span></span>'
      );
      numArray[numArray.length] = parseInt(numChars[i], 10);
    } else {
      obj.append("<span>" + numChars[i] + "</span>");
    }
  }

  // determine the height of each number for the animation
  var increment = obj.find(".digit-con").outerHeight();
  var speed = 2000;

  // animate each number
  for (var i = 0; i < numArray.length; i++) {
    obj
      .find(".digit" + i)
      .animate({
          top: -(increment * numArray[i])
        },
        Math.round(speed / (1 + i * 0.333))
      );
  }
}

$(window).scroll(function() {
  const counterNumbers = $(".number").toArray();
  counterNumbers.filter(isVisible).map(Counter);
});
$(window).trigger("scroll");
.number {
  display: block;
  font-size: 6rem;
  line-height: 6.5rem;
}

.number *+* {
  margin-top: 0;
}

.digit-con {
  display: inline-block;
  height: 6.5rem;
  overflow: hidden;
  vertical-align: top;
}

.digit-con span {
  display: block;
  font-size: 6rem;
  line-height: 6.5rem;
  position: relative;
  text-align: center;
  top: 0;
  width: 0.55em;
}

.month {
  height: 100vh;
}
<h1>Scroll</h1>
<div class="number">$2,350,354.43</div>
<div class="month">March</div>
<div class="number">$6,350,354.43</div>
<div class="month">March</div>
<div class="number">$8,500,435.33</div>
<div class="month">April</div>
<div class="number">$3,500,435.53</div>
<div class="month">May</div>

<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js'></script>

1 Answers

Your issue is a something similar to FOUC - Flash of Unstyled Content - where what's originally in the HTML is displayed before it can be updated.

This can be fixed by changing the html and showing the value you want to display on load, while storing the required number in a data- attribute, eg:

<div class="number" data-number="$2,350,354.43">$0,000,000.00</div>

with a small change to your existing code to read the data- instead of text, from

var number = obj.text();
obj.attr("data-number", number);  // this is never used

to

var number = obj.data("number");

If you can't change the html (or don't want to) then you can have an initialisation script run before your first scroll initialisation:

$(".number").each((i,e) => {
  var obj = $(e);
  var number = obj.text();
  obj.data("number", number);        
  obj.text(number.replace(/\d/g, "0"));
});

You will still get FOUC on the very first counter if it's already visible because that's how javascript works: to keep things simple/basic: the page is rendered, then js runs. So there's a short time before the js runs where it's parsing/processing the js ready to run - how long this will be depends on how much js you have (including libraries) / whether it's cached / how much initialisation code there is.

Generally better to output your HTML as you want it displayed rather than rely on JS to update it, but that's not always possible.

Updated snippet:

function isVisible(el) {
  const element = $(el);
  var WindowTop = $(window).scrollTop();
  var WindowBottom = WindowTop + $(window).height();
  var ElementTop = element.offset().top;
  //var ElementBottom = ElementTop + element.height();
  var ElementBottom = ElementTop + 20;
  return ElementBottom <= WindowBottom && ElementTop >= WindowTop;
}

function Counter(el) {
  obj = $(el);
  if (obj.hasClass("ms-animated")) {
    return;
  }
  obj.addClass("ms-animated");

  // get the number
  //var number = obj.text();
  //obj.attr("data-number", number);
  var number = obj.data("number");

  // clear the HTML element
  obj.empty();

  // create an array from the text, prepare to identify which characters in the string are numbers
  var numChars = number.split("");
  var numArray = [];
  var setOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  // for each number, create the animation elements
  for (var i = 0; i < numChars.length; i++) {
    if ($.inArray(parseInt(numChars[i], 10), setOfNumbers) != -1) {
      obj.append(
        '<span class="digit-con"><span class="digit' +
        numArray.length +
        '">0<br>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br></span></span>'
      );
      numArray[numArray.length] = parseInt(numChars[i], 10);
    } else {
      obj.append("<span>" + numChars[i] + "</span>");
    }
  }

  // determine the height of each number for the animation
  var increment = obj.find(".digit-con").outerHeight();
  var speed = 2000;

  // animate each number
  for (var i = 0; i < numArray.length; i++) {
    obj
      .find(".digit" + i)
      .animate({
          top: -(increment * numArray[i])
        },
        Math.round(speed / (1 + i * 0.333))
      );
  }
}

$(window).scroll(function() {
  const counterNumbers = $(".number").toArray();
  counterNumbers.filter(isVisible).map(Counter);
});

$(".number").each((i,e) => {
  var obj = $(e);
  var number = obj.text();
  obj.data("number", number);        
  obj.text(number.replace(/\d/g, "0"));
});

$(window).trigger("scroll");
.number {
  display: block;
  font-size: 6rem;
  line-height: 6.5rem;
}

.number *+* {
  margin-top: 0;
}

.digit-con {
  display: inline-block;
  height: 6.5rem;
  overflow: hidden;
  vertical-align: top;
}

.digit-con span {
  display: block;
  font-size: 6rem;
  line-height: 6.5rem;
  position: relative;
  text-align: center;
  top: 0;
  width: 0.55em;
}

.month {
  height: 100vh;
}
<h1>Scroll</h1>
<div class="number">$2,350,354.43</div>
<div class="month">March</div>
<div class="number">$6,350,354.43</div>
<div class="month">March</div>
<div class="number">$8,500,435.33</div>
<div class="month">April</div>
<div class="number">$3,500,435.53</div>
<div class="month">May</div>

<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js'></script>

Related