1

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>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
test
  • 73
  • 7
  • 1
    Welcome to Stack Overflow! I've copied your HTML and JavaScript into a Stack Snippet (the `[<>]` toolbar button); [here's how to do one](https://meta.stackoverflow.com/questions/358992/), but it doesn't seem to do anything. Could you update it to make it a [mcve] with instructions for how to see the problem? I think it needs something to scroll, but I don't want to guess at what you're really using for that. – T.J. Crowder Feb 26 '21 at 09:28
  • Thank you very much. It seems to me that it does not work in this case because this cannot be scroll through it. – test Feb 26 '21 at 09:31
  • Now, with the p tag it's working. – test Feb 26 '21 at 09:32
  • Just tried in Safari 14.0.3 and it seems to work as expected. – RobG Feb 26 '21 at 09:33
  • I tested on mobile safari on ios 14.4 and it's shows NaN only in the big number – test Feb 26 '21 at 09:34
  • I can replicate the problem on iOS Safari as well. – T.J. Crowder Feb 26 '21 at 09:36
  • @RobG - Seems to just be iOS Safari. – T.J. Crowder Feb 26 '21 at 09:45

2 Answers2

2

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.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • This does not seem very efficient `if (parseInt(el.textContent, 10) !== currentCount) { el.textContent = currentCount.toLocaleString().replace(/,/g, ' '); }` - why is this not a different var instead of textContent? – mplungjan Feb 26 '21 at 09:40
  • 1
    @mplungjan - I didn't get into anything other than the NaN problem. – T.J. Crowder Feb 26 '21 at 09:41
  • Well the original was `if (parseInt(el.innerHTML, 10) ` so if we did not use the DOM as storage at all, then the issue had been solved too – mplungjan Feb 26 '21 at 09:42
  • @test - I wouldn't expect it either, I find this behavior of iOS Safari **very** surprising. Showing the number as a phone number would be fine, but actually changing the DOM to add the markup for that seems...less than ideal. :-) – T.J. Crowder Feb 26 '21 at 09:45
0

Why not use counto instead of using DOM as a storage for the number to counto?

And as TJ said, the issue was Safari's handling of your number when using innerHTML instead of textContent

if (currentCount<=countTo) {
  el.textContent = currentCount.toLocaleString().replace(/,/g, ' ');
}

Tested on iOS 14.4

https://plungjan.name/SO/tl.html

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); // do not use innerHTML here
  var counter = setInterval(() => {
    frame++;
    var progress = easeOutQuad(frame / totalFrames);
    var currentCount = Math.round(countTo * progress);
    if (currentCount<=countTo) {
      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>
mplungjan
  • 169,008
  • 28
  • 173
  • 236