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.

3
  • can you use any library? Commented Apr 1, 2017 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 minimal reproducible example and How to Ask. You should also include a description what you have tried to do to fix the problem so far. Commented Apr 1, 2017 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 <script>. Language is deprecated, was never standardized and is unneeded; script always defaults to JS. Also, CSS doesn't support single-line style // comment comments, you need to use multi-line style /* comment */ comments in CSS. Commented Apr 1, 2017 at 15:17

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

Comments

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

14 Comments

I'm trying to adhere to the requirements, but forgot to include the code...sorry.
@DouglasBenoit Does my answer help you in any sort? And if it does then please do give it thumbs up :)
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)
of course, I modified the original page, and saved it under a different name: html-experiments/countdown04_flip_clock.htm
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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.