Search code examples
javascriptmobile-safarinan

Large numbers in count-up show NaN in safari browser


I have created a count-up function in Vanilla JS. Everything works well. The counter starts when you scroll down to it. Unfortunately the big number 35 000 000 shows up as NaN in the iOS Safari browser. I have no idea why. Please help. It looks like large numbers don't work in iOS Safari browser.

var animationDuration = 3000;
var frameDuration = 1000 / 60;
var totalFrames = Math.round(animationDuration / frameDuration);
var easeOutQuad = t => t * (2 - t);
var animateCountUp = el => {
  let frame = 0;
  var countTo = parseInt(el.innerHTML.replace(/ /g, ''), 10);
  var counter = setInterval(() => {
    frame++;
    var progress = easeOutQuad(frame / totalFrames);
    var currentCount = Math.round(countTo * progress);
    if (parseInt(el.innerHTML, 10) !== currentCount) {
      el.textContent = currentCount.toLocaleString().replace(/,/g, ' ');
    }
    if (frame === totalFrames) {
      clearInterval(counter);
    }
  }, frameDuration);
};
var count = document.querySelectorAll('.countup'),
  once = 1;
document.addEventListener('scroll', function() {
  if (once == 1 && count[0].getBoundingClientRect().top < window.innerHeight) {
    once = 0;
    count.forEach(animateCountUp);
  }
});
<div style="position: fixed">
    <span class="countup">12 500</span>
    <span class="countup">35 000 000</span>
</div>
<div style="height: 5000px"></div>


Solution

  • Using innerHTML for this is slightly less than ideal, and in fact iOS Safari is adding markup to your large number which is tripping up your code. It's identifying it as a telephone number and changing the inner HTML of that element to: <a href="tel:35 000 000">35 000 000</a>. That's very surprising, but you can see it here:

    console.log(document.querySelector(".countup").innerHTML);
    <span class="countup">35 000 000</span>

    Note: Only on iOS Safari as far as I can tell.

    Using textContent instead works, since the text is unchanged:

    var animationDuration = 3000;
    var frameDuration = 1000 / 60;
    var totalFrames = Math.round(animationDuration / frameDuration);
    var easeOutQuad = t => t * (2 - t);
    var animateCountUp = el => {
      let frame = 0;
      var countTo = parseInt(el.textContent.replace(/ /g, ''), 10);
      var counter = setInterval(() => {
        frame++;
        var progress = easeOutQuad(frame / totalFrames);
        var currentCount = Math.round(countTo * progress);
        if (parseInt(el.textContent, 10) !== currentCount) {
          el.textContent = currentCount.toLocaleString().replace(/,/g, ' ');
        }
        if (frame === totalFrames) {
          clearInterval(counter);
        }
      }, frameDuration);
    };
    var count = document.querySelectorAll('.countup'),
      once = 1;
    document.addEventListener('scroll', function() {
      if (once == 1 && count[0].getBoundingClientRect().top < window.innerHeight) {
        once = 0;
        count.forEach(animateCountUp);
      }
    });
    <div style="position: fixed">
        <span class="countup">12 500</span>
        <span class="countup">35 000 000</span>
    </div>
    <div style="height: 5000px"></div>

    You can also tell iOS Safari not to do this by adding a meta tag as described in this question's answers:

    <meta name="format-detection" content="telephone=no">
    

    but as far as I can tell that disables it page-wide.