Search code examples
javascripthtmldjangodatatables

How can i add different timers as a column in bootstrap datatables rows according to date given?


So, I am trying to add a countdown timer on a table that gets the value of the due date and adds a cell showing the remaining time. Currently, it works with one record but on adding two or more rows, I get a replica of the last row timer duplicating on all the records' timer cells

<div class="card mb-3">
  <div class="card-header">
    <i class="fa fa-table"></i> <sup><span class="badge badge-warning"> {{ new}} New</span></sup>New Orders From Clients
  </div>
  <div class="card-body">
    <div class="table-responsive">
      <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
        <thead>
          <td>Reference Code</td>
          <td>Title</td>
          <td>Number of Pages</td>
          <td>Take this Task</td>
          <td>Due Date</td>
          <td>Remaining Time</td>
        </thead>
        {% for t in order %}
        <tbody>
          <tr>
            <td>
              <a href="{% url 'view_order' pk=t.pk %}">{{ t.urlhash }}</a>
            </td>
            <td>{{ t.title }}</td>
            <td>{{ t.Number_of_pages }}</td>
            <td>
              {% if t.status == False %}
              <a href="{% url 'claim' pk=t.pk %}" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm">Take order</a>{% else %}
              <span>&#10004; Taken</span> {% endif %}
            </td>
            <td>{{ t.due_date }} </td>
            <td id="time" class="text-warning"></td>
          </tr>
          <script>
            // Set the date we're counting down to
            var countDownDate = new Date("{{ t.due_date.isoformat }}").getTime();
            // console.log(dex);
            //var countDownDate = new Date("July 18, 2020 19:27:25").getTime();
            // 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="time"
              document.getElementById("time").innerHTML = "Remaining time " + days + " Days " + hours + " hours " + minutes + " minutes " + seconds + "s ";
              // If the count down is finished, write some text
              if (distance < 0) {
                clearInterval(x);
                document.getElementById("time").innerHTML = "EXPIRED";
              }
            }, 1000);
          </script>
          {% endfor %}
        </tbody>
      </table>
    </div>
  </div>
</div>
the aboe code works well with one record but on adding two or more only the first row shows the timer with the others as empty.If i make the css dynamic and unique across each row by giving the reference code it replicates across each row as one timer

I don't understand what's the problem.


Solution

  • This isn't really a DataTables question but it was fun so this is what I worked up:

    (() => {
      setInterval(() => {
        const table = document.getElementById("dataTable")
        for (var i = 0, row; row = table.rows[i]; i++) {
          const dueDate = row.querySelector('.due-date')
          if (dueDate) {
            const dateBits = dueDate.innerHTML.split('/')
            const dateDue = new Date(dateBits[2], dateBits[1], dateBits[0]).getTime()
            const now = new Date().getTime()
            const distance = dateDue - now
            const countDownDate = row.querySelector('.text-warning')
            if (distance < 0) {
              countDownDate.innerHTML = 'EXPIRED'
            } else {
              const days = Math.floor(distance / (1000 * 60 * 60 * 24));
              const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
              const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
              const seconds = Math.floor((distance % (1000 * 60)) / 1000);
              countDownDate.innerHTML = `Remaining time ${days} Days ${hours} hours ${minutes} minutes ${seconds}s`
            }
          }
        }
      }, 1000)
    })()
    

    What we're doing here is grabbing the table every second, iterating through the table rows and checking to see if there's the appropriate cell called due-date. If there is then we check the distance, if it's negative then we set the cell with the class text-warning to be EXPIRED otherwise, we add the appropriate message. I hope that helps? Working JSFiddle here.