1

The scripts on this question were working great for me, until I had numbers that needed to include thousands separators. Now I get an error "NAN" for those. I need the separators to be there. Not sure how to solve this. The only (hacky) thing I can think of is to just have counts for every group between the separators, so for 1,160,868, it would be like

<span class="count">1</span>,<span class="count">160</span>,<span class="count">868</span>




// Counter animation
// inViewport jQuery plugin
// http://stackoverflow.com/a/26831113/383904
$(function($, win) {
   $.fn.inViewport = function(cb) {
      return this.each(function(i,el){
         function visPx(){
            var H = $(this).height(),
            r = el.getBoundingClientRect(), t=r.top, b=r.bottom;
            return cb.call(el, Math.max(0, t>0? H-t : (b<H?b:H)));
         } visPx();
         $(win).on("resize scroll", visPx);
      });
   };
}(jQuery, window));

jQuery(function($) { // DOM ready and $ in scope
   $(".count").inViewport(function(px) { // Make use of the `px` argument!!!

      // if element entered V.port ( px>0 ) and
      // if prop initNumAnim flag is not yet set
      //  = Animate numbers
      if(px>0 && !this.initNumAnim) {
         this.initNumAnim = true; // Set flag to true to prevent re-running the same animation
         $(this).prop('Counter',0).animate({
            Counter: $(this).text()
         }, {
            duration: 4000,
            step: function (now) {
               $(this).text(Math.ceil(now));
            }
         });
      }
   });
});
// end Counter animation

Which would work, technically. And if that's the way I need to do it, I will. But it would be a whole lot easier for my backend team, when they're pulling these numbers from the database, to be able to just grab the number with the comma separators.

Laura Sage
  • 201
  • 1
  • 11
  • need more to go on. Whatever script you need help with should be in your question, formatted – Kinglish Jul 31 '21 at 20:59
  • I apologize. I thought by linking to the original question where my script came from, that was sufficient. Adding the script I'm using now. – Laura Sage Jul 31 '21 at 22:13
  • @LauraSage If I got your questions correctly, you want to "animate" numbers from 0 to N but having them formatted in that specific `SPAN.count`s HTML markup with commas in between, right? – Roko C. Buljan Jul 31 '21 at 22:14
  • @RokoC.Buljan Yes. And I've added the script I'm using now. – Laura Sage Jul 31 '21 at 22:19

1 Answers1

1

To get your thousands - comma separated string use Number.prototype.toLocaleString()

console.log( (12345678).toLocaleString('en-US') );

Animate when in viewport

For your specific case, instead of using my old inViewport code snippet from this answer, I might suggest to use the IntersectionObserver API.

If you need the groups as individual SPAN elements - split the result of toLocaleString at commas, wrap each value into a <span> and finally join(",") the Array back into a string:

const animNum = (EL) => {
  
  if (EL._isAnimated) return; // Animate only once!
  EL._isAnimated = true;
  
  $(EL).prop('Counter', 0).animate({
    Counter: EL.dataset.num
  }, {
    duration: 3000,
    step: function(now) {
      const text = (Math.ceil(now)).toLocaleString('en-US');
      const html = text.split(",").map(n => `<span class="count">${n}</span>`).join(",");
      $(this).html(html);
    }
  });
};

const inViewport = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) animNum(entry.target);
  });
};

$("[data-num]").each((i, EL) => {
  const observer = new IntersectionObserver(inViewport);
  observer.observe(EL);
});
.count {
  font-size: 1.5em;
  color: fuchsia;
}
<p style="height: 200vh;">Scroll down.......</p>
<div data-num="12345678"></div>
<p style="height: 200vh;">Scroll down.......</p>
<div data-num="7536712"></div>
<p style="height: 100vh;">That's it!</p>

<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • So, I did find one problem. The count seems to happen every time I scroll back into view, and I only want it to do it the first time it comes into view. After that it should just stay at the final count. Any easy way to solve that? The previous one seemed to do that already. – Laura Sage Aug 01 '21 at 03:26
  • @LauraSage One easy way is to store into an Element property that it was already animated, and if that was the case, early-exit from the animNum function. I'll add it into the code answer – Roko C. Buljan Aug 01 '21 at 10:05