0

I found a way to display multiple countdowns, using javascript. Having multiple countdown timer events intervals in javascript

I am trying to create a "warmer" display, in a grid. I opted for an HTML table. I have a draft here

I can't get the countdowns to display in the table cells, with separated time components. I just have the entire SPAN tag in the "Days" column.

Community
  • 1
  • 1
  • can you use any library? – Samarth Apr 01 '17 at 14:59
  • 2
    Welcome to SO! Here it is considered good practice to include the relevant part of your code in the question, as stuff at linked URLs can change or disappear in the future and make your question less useful to anyone who might come across it in the future. People are likely to ignore or downvote your question if you don't. Please read [mcve] and [ask]. You should also include a description what you have tried to do to fix the problem so far. – Useless Code Apr 01 '17 at 15:03
  • A couple of other things. There is no need to use the `language` attribute on a script tag, just use a bare ` – Useless Code Apr 01 '17 at 15:17

2 Answers2

1

I'd take a different approach. First of all, instead of creating the table in your HTML, I would store the data about the countdown timers in an array of objects in your JavaScript code and generate the table using JS. Doing this will make it cleaner and more maintainable; to add new items you can just add an object to the array instead of mucking about with HTML.

Secondly, instead of starting an interval for each timer, I would create a single interval that updates all of the timers. Using a single interval means your DOM updates will be batched together and will minimize page reflow.

This code also recalculates the time left each time it updates. If you calculate it once and then just subtract each round, it could introduce drift into your counter. This is because setInterval only guarantees that it will wait at least as many milliseconds as you specify in the delay parameter, it could be more. It probably wouldn't matter much unless your timer was running continuously for a very long time, but over time it would be come inaccurate.

// data is an array of objects, each representing one of your categories.
// Each category has a .title to store its title and a .counters that
// stores an object for each counter in that category.
var data = [
  {
title: 'ASTRONOMY',
counters: [
  // Each counter has a .title and a .date that is parsed by new Date()
  {
    title: 'Total Solar Eclipse - (NE US)',
    date: 'August 21, 2017'
  },
  {
    title: 'Next Supermoon - Full',
    date: 'December 3, 2017'
  }
]
  },
  {
title: 'POLITICS',
  counters: [
    {
      title: 'Next Presidential Election',
      date: 'November 3, 2020'
    }
  ]
  },
  {
title: 'TEST',
counters: [
  {
    title: '30 seconds from page load',
    date: (new Date()).getTime() + (30 * 1000)
  },
  {
    title: 'Unix Epoch',
    date: 'January 1, 1970'
  }
]
  }
];
// this reduce generates the table
let table = data.reduce((acc, category, categoryIndex) => {
return acc + `<tr><td colspan="6" class="category">${category.title}</td></tr>` +
category.counters.reduce((acc, counter, index) => {
  return acc + `<tr id="counter-${categoryIndex}-${index}">
  <td>${counter.title}</td>
  <td>${counter.date}</td>
  <td class="days"></td>
  <td class="hours"></td>
  <td class="minutes"></td>
  <td class="seconds"></td>
  </tr>`;
  }, '');
}, '<table class="countdown"><tr><th>Event</th><th>Date</th><th>Days</th><th>Hours</th><th>Minutes</th><th>Seconds</th></tr>');
table += '</table>';

// insert the table after the noscript tag
document.getElementById('countdown').insertAdjacentHTML('afterend', table);

// generate a flat list of counters
let counters = data.reduce((acc, category, categoryIndex) => {
return acc.concat(category.counters.reduce((counterAcc, counter, index) => {
    return counterAcc.concat([{
      // counters will be an array of the objects we generate here.
      // node contains a reference to the tr element for this counter
      node: document.getElementById(`counter-${categoryIndex}-${index}`),
      // date is the date for this counter parsed by Date and then converted
      // into a timestamp
      date: (new Date(counter.date)).getTime()
      }]);
    }, []));
}, []);

const msSecond = 1000,
  msMinute = msSecond * 60,
  msHour = msMinute * 60,
  msDay = msHour * 24;
let intervalId;

function updateCounters () {
  counters.forEach((counter, counterIndex) => {
  let remaining = counter.date - Date.now(),
    node = counter.node;
  let setText = (selector, text) => node.querySelector(selector).textContent = text;

  if (remaining > 0) {
    setText('.days', Math.floor(remaining / msDay));
    remaining %= msDay;
    setText('.hours', Math.floor(remaining / msHour));
    remaining %= msHour;
    setText('.minutes', Math.floor(remaining / msMinute));
    remaining %= msMinute;
    setText('.seconds', Math.floor(remaining / msSecond));
  } else {
    // make sure we don't accidentally display negative numbers if a timer
    // firing late returns a past timestamp (or the data contains a past date)
    setText('.days', 0);
    setText('.hours', 0);
    setText('.minutes', 0);
    setText('.seconds', 0);

    // This countdown has reached 0 seconds, stop updating it.
    counters.splice(counterIndex, 1);
    // no more counters? Stop the timer
    if (counters.length === 0) {
      clearInterval(intervalId);
    }
  }
  });
}
// display counters right away without waiting a second
updateCounters();
intervalId = setInterval(updateCounters, 1000);
 table {
 border-collapse: collapse;
 }
 tr:nth-child(even) {
 background-color: #edf;
 }
 .category {
 font-weight: bold;
 }
 td, th {
 padding: .5em;
 }
 .days, .hours, .minutes, .seconds {
 text-align: right;
 }
 <noscript id="countdown">Sorry, you need JavaScript enabled to view the count
 downs</noscript>

More Reading

Useless Code
  • 12,123
  • 5
  • 35
  • 40
0

If you are ok to use any javascript library why not check FlipClock.js out.

As per the text provided on their site, the following are the logical requirements that were considered when creating the API.

  • Use as a clock
  • Use as a timer
  • Use as a countdown
  • Themeable using pure CSS
  • Clean & Dry Syntax
  • Abstract everything into reusable objects
  • Full-Featured Developer API to create custom “Clock Faces”

And if you are not ok to use any library here is what you can learn from about how to create a countdown timer using javascript

Samarth
  • 773
  • 1
  • 6
  • 14
  • I'm trying to adhere to the requirements, but forgot to include the code...sorry. – Douglas Benoit Apr 01 '17 at 17:50
  • @DouglasBenoit Does my answer help you in any sort? And if it does then please do give it thumbs up :) – Samarth Apr 01 '17 at 17:54
  • I'm trying to adhere to the requirements, but forgot to include the code...sorry. I visited the flipclockjs page, and I was very excited. Thank you. While I find that flip clock to be awesome, I feel it would be too much to have a flip clock to the right of each calendar item, but, I could use the flip clock at the top of my calendar, linked to the "Next" calendar line item. (Who needs more than one swimming pool in their backyard?) However, Somehow - and I do not know how - the first column countdown is working on my webpage now. But, it's missing the hours and days.... (char max now) – Douglas Benoit Apr 01 '17 at 17:58
  • of course, I modified the original page, and saved it under a different name: html-experiments/countdown04_flip_clock.htm – Douglas Benoit Apr 01 '17 at 18:02
  • you can checkout w3school link provided too in case flipclock is heavy for your requirement. They have shown how to create similar counter you would in just javascript. – Samarth Apr 01 '17 at 18:02
  • I visited your new link there is some problem with getting http://www.benoitsystems.com/Family/Doug/compiled/flipclock.css http://www.benoitsystems.com/Family/Doug/compiled/flipclock.js – Samarth Apr 01 '17 at 18:06
  • How do I give the thumbs up? I clicked on your name, but I'm lost. – Douglas Benoit Apr 01 '17 at 18:14
  • If you like my answer just give vote up by clicking up arrow next to my name and if my answer meets your question requirement then just click on the tick mark beside my answer. Thanks :) – Samarth Apr 01 '17 at 18:16
  • In regard to "benoitsystems.com/Family/Doug/compiled/flipclock.css" I do not know where to obtain the css doc, from the downloaded flipclockjs.zip file. – Douglas Benoit Apr 01 '17 at 18:17
  • put the **compiled/flipclock.js** and **compiled/flipclock.css** from the FlipClock unzipped folder into the folder where you would put your other css and js file. – Samarth Apr 01 '17 at 18:19
  • I need to now learn how to avoid extended discussions in comments (as this thread indicates), and then learn to move this discussion to chat. First, it says I cannot yet move it to chat.... – Douglas Benoit Apr 01 '17 at 18:38
  • @DouglasBenoit is it working for you now or still looking for further help..am glad if I could help. – Samarth Apr 01 '17 at 18:42
  • The W3Schools version maxes out with four calendar entries, hard-coded into the js. Is there such a thing as a countdown which references the value in the table's in the same row? – Douglas Benoit Apr 01 '17 at 18:46
  • @DouglasBenoit I guess you can accept this by clicking on the tick mark on the left hand side of the answer :) – Samarth Apr 02 '17 at 17:32