javascripthtmlwebjavascript-objects

Countdown Timer Gets Synced Across all Elements, not Individually


I have the following script that is a countdown timer that display the hours, minutes, seconds (00:00:00:00) remaining until reaching a specified date. On my HTML page, I have 4 objects that have the timer. The script works, however the timer is the same across all 4 objects, despite each having unique values that should change the countdown. Forgive me as I'm actually working in Webflow, however hopefully it poses no issue:

<script>
  // all the objects with the timer
  var wrappers = document.querySelectorAll(".collection-item");
  wrappers.forEach(function(wrapper) {
    var countDownDate = new Date("July 15, 2023 00:00:00").getTime();

    var x = setInterval(function() {
      var now = new Date().getTime();
      var distance = countDownDate - now;

      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);

      // update the HTML elements within the current wrapper
      wrapper.querySelector("#day").innerHTML = days;
      wrapper.querySelector("#hour").innerHTML = hours;
      wrapper.querySelector("#minute").innerHTML = minutes;
      wrapper.querySelector("#second").innerHTML = seconds;

      if (distance < 0) {
        clearInterval(x);
        carWrapper.querySelector(".announcement").innerHTML = "Yay!";
      }
    }, 1000);
  });
</script> 

Each object is part of a collection list in Webflow, seen with the following HTML:

<div role="listitem" class="collection-item w-dyn-item">
   <a id="000001" href="/books/000001" target="_blank" class="wrapper w-inline-block">
      <h1 class="something">
        <span id="day">00</span>
        <span id="hour">00</span>
        <span id="minute">00</span>
        <span id="second">00</span>
      </h1>
   </a>
      <script>
        script from above here
      </script>
</div>

Not sure what's wrong/how to get each timer to run independently for each object. Again, the timer is counting down properly, but all 4 objects are using the time of the 4th object, instead of their own.


Solution

  • I'm not familiar with webflow but that appears to be a template fragment that gets rendered once for every item.
    You only want your javascript to run once since it is updating every .collection-item, or have it target only the current item.
    I would suggest rendering the date value of the item in a data-attribute and include your script once for the entire collection reading each items value from the html.

    // all the objects with the timer
    var wrappers = document.querySelectorAll(".collection-item");
    wrappers.forEach(function(wrapper) {
      //get the item's data-date
      var countDownDate = new Date(wrapper.querySelector('a').dataset['date']).getTime();
    
      var x = setInterval(function() {
        var now = new Date().getTime();
        var distance = countDownDate - now;
    
        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);
    
        // update the HTML elements within the current wrapper
        wrapper.querySelector("#day").innerHTML = days;
        wrapper.querySelector("#hour").innerHTML = hours;
        wrapper.querySelector("#minute").innerHTML = minutes;
        wrapper.querySelector("#second").innerHTML = seconds;
    
        if (distance < 0) {
          clearInterval(x);
          carWrapper.querySelector(".announcement").innerHTML = "Yay!";
        }
      }, 1000);
    });
    <div role="listitem" class="collection-item w-dyn-item">
      <a id="000001" href="/books/000001" target="_blank" class="wrapper w-inline-block" data-date="July 15, 2023 00:00:00">
        <h1 class="something">
          <span id="day">00</span>
          <span id="hour">00</span>
          <span id="minute">00</span>
          <span id="second">00</span>
        </h1>
      </a>
    </div>
    <div role="listitem" class="collection-item w-dyn-item">
      <a id="000001" href="/books/000001" target="_blank" class="wrapper w-inline-block" data-date="July 25, 2023 00:00:00">
        <h1 class="something">
          <span id="day">00</span>
          <span id="hour">00</span>
          <span id="minute">00</span>
          <span id="second">00</span>
        </h1>
      </a>
    </div>
    <div role="listitem" class="collection-item w-dyn-item">
      <a id="000001" href="/books/000001" target="_blank" class="wrapper w-inline-block" data-date="July 15, 2024 12:30:30">
        <h1 class="something">
          <span id="day">00</span>
          <span id="hour">00</span>
          <span id="minute">00</span>
          <span id="second">00</span>
        </h1>
      </a>
    </div>
    
    <script>
      // "script from above" here only once, it should be safe even when the collection is empty
    </script>