Multiple consecutive 5 minute countdown timers

Viewed 134

I want to make 3 countdown timers where the next one starts when the last one ends (the first timer will start counting down the time automatically, but the second timer will only start when the first one reaches 0:00 and the third one will only start when the second one reaches 0:00).

I found this code for a countdown timer:

    function countDown() {
        var seconds = 60; 
        var mins = 5;
        function clickClock() {
            var counter = document.getElementById("countdown1");
            var currentMinutes = mins - 1; 
            seconds--; 
            counter.innerHTML = currentMinutes.toString() + ":" + (seconds < 10 ? "0" : "") + String(seconds);
            if(seconds > 0) {
                setTimeout(clickClock, 1000);
            } else {
                if(mins > 1) {
                    countDown(mins-1);
                }
            }
        }
        clickClock();
    }
    countDown();

On my HTML, I have 3 spans, each with a unique ID (#countdown1, #countdown2, #countdown3)

I have tried passing in an parameter to the clickClock() function called counter so that whenever I called the function I could enter the id of the element I wanted to affect, didn't work.

I could just make 2 other functions that would do exactly the same thing but would change the counter variable, but I'd like to avoid repeating unnecessary things in my code.

How could this be done?

Any help is appreciated :)

1 Answers

I'd do something like this:

/////////// USAGE
const timersDurationInMilliseconds = 1000 * 5; // for 5 minutes do: 1000 * 60 * 5

// Render initial timer content
RenderTimer('countdown1', timersDurationInMilliseconds);
RenderTimer('countdown2', timersDurationInMilliseconds);
RenderTimer('countdown3', timersDurationInMilliseconds);

// Start countdown, then start another ones
countDown(timersDurationInMilliseconds, 'countdown1')
  .then(() => countDown(timersDurationInMilliseconds, 'countdown2'))
  .then(() => countDown(timersDurationInMilliseconds, 'countdown3'))
  .then(() => alert('All timers finished!'));


/////////// REQUIRED METHODS
function countDown(durationInMilliseconds, elementId) {
  return new Promise((resolve, reject) => {
    const updateFrequencyInMilliseconds = 10;
    const currentTimeInMilliseconds = new Date().getTime();
    const endTime = new Date(currentTimeInMilliseconds + durationInMilliseconds);
    
    function updateTimer(elementId) {
      let timeLeft = endTime - new Date();
      if (timeLeft > 0) {
        // We're not done yet!
        setTimeout(updateTimer, updateFrequencyInMilliseconds, elementId);
      } else {
        // Timer has finished!
        resolve();
        
        // depending on update frequency, timer may lag behind and stop few milliseconds too late
        // this will cause timeLeft to be less than 0
        // let's reset it back to 0, so it renders nicely on the page
        timeLeft = 0;
      }
      
      RenderTimer(elementId, timeLeft);
    }

    updateTimer(elementId);
  });
}

function padNumber(number, length) {
  return new String(number).padStart(length || 2, '0'); // adds leading zero when needed
}

function RenderTimer(elementId, timeLeft) {
  const hoursLeft = Math.floor(timeLeft / 1000 / 60 / 60 % 60);
  const minutesLeft = Math.floor(timeLeft / 1000 / 60 % 60);
  const secondsLeft = Math.floor(timeLeft / 1000 % 60);
  const millisecondsLeft = timeLeft % 1000;

  const counterElement = document.getElementById(elementId);
  counterElement.innerHTML = `${padNumber(hoursLeft)}:${padNumber(minutesLeft)}:${padNumber(secondsLeft)}.${padNumber(millisecondsLeft, 3)}`;
}
<p>First countdown: <span id="countdown1"></span></p>
<p>Second countdown: <span id="countdown2"></span></p>
<p>Third countdown: <span id="countdown3"></span></p>

If you want to only display minutes and seconds, you can adjust that behavior in RenderTimer method.

If you don't plan on displaying milliseconds to the user, you may want to change how frequent the timer is updated and rendered on the page by adjusting the updateFrequencyInMilliseconds variable (e.g. from 10ms to 1000ms).

Related