Skip to content
Advertisement

Can jQuery .each be used when setting an interval within that sets an element’s .html()?

I have a page with multiple .countdown-element divs which I want to contain a countdown.

I am iterating through each .countdown-element and attempting to apply a countdown timer to each of them.

The $(this).html() parts in the setInterval do not trigger. I assume this is related to using .each and it’s going through the other .countdown-element divs before it can run.

How can I get the countdown to be applied to each of the .countdown-element divs?

$( '.countdown-element' ).each( function() {

    // Set the date we're counting down to
    var countDownDate = new Date( $(this).attr('data-countdown-to') ).getTime();
    console.log( 'test: ' + countDownDate );

    // Update the count down every 1 second
    var x = setInterval(function() {

      // Get today's date and time
      var now = new Date().getTime();

      // Find the distance between now and the count down date
      var distance = countDownDate - now;

      // Time calculations for days, hours, minutes and seconds
      var days = Math.floor(distance / (1000 * 60 * 60 * 24));
      var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
      var seconds = Math.floor((distance % (1000 * 60)) / 1000);

      // Display the result in the element with id="demo"
      $(this).html( days + "d " + hours + "h " + minutes + "m " + seconds + "s " );

      // If the count down is finished, write some text
      if (distance < 0) {
        clearInterval(x);
        $(this).html( 'EXPIRED' );
      }
    }, 1000);

});

Div markup is (note the data attribute here is dynamic from PHP hence why I need to apply it as an attribute and use that from the JS):

<div class="countdown-element" data-countdown-to="2021-02-10 12:00"></div>'

Advertisement

Answer

The this inside the setInterval is callback is not the same this as inside the .each

Take a copy of this inside the .each and use that, eg:

$( '.countdown-element' ).each( function() {

    var countdownElement = $(this);

    var timer = setInterval(function() {

      // ...

      countdownElement.html(days + "d " + hours + "h " + minutes + "m " + seconds + "s ");

      // If the count down is finished, write some text
      if (distance < 0) {
        clearInterval(timer);
        countdownElement.html('EXPIRED');
      }
    }, 1000);

});

Alternatively, depending on your browser, use => to preserve this into the callback

var i = 1;
$("div").each(function() {

    // => arrow function preserves `this` from outer 
    
    var timer = setInterval(() => {
        $(this).html(timer + "," + i)    
      
        // just a method to break out
        if (i++>20) clearInterval(timer);
        
    }, 100);
    
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div></div>
<div></div>
<div></div>
Advertisement