Using setInterval() to build a countdown

Hello,

I am working on a self directed project to practice the skills I’ve learned so far (I’ve finished the first 3 projects in the Javascript beta course.

My program is a “Simon Says” game that flashes a pattern of colors in one box and the player has to select that pattern of colors on the provided square.

I want a count down timer to change the text of a centered element in the flash box so that the browser displays 3… 2… 1 with 1 second between each dynamic update of the html content.

I tried to accomplish this with setInterval, passing in a custom function that successfully updates the textContent of the desired element, however, the program does not appear to pause like the documentation says setInterval should. I put setInterval into a loop that breaks at the appropriate time so it gets called the right number of times, but there is no delay, it just races through to 0 instantly.

Any help much appreciated!

// VARIABLES
const startButton = document.getElementById('start-button');
const simonBox = document.getElementsByClassName('simon');
const counter = document.getElementById('countdown');

// FUNCTIONS
console.log(counter)

const countDown = (count) => {
    counter.textContent = count.toString();
}

const beginCountDown = () => {
    let count = 3;
    while (count > 0) {
    setInterval(countDown(count), 1000);
    count--;
    }
}

const startGame = () => {
    startButton.style.display = 'none';
    counter.style.display = 'block';
    beginCountDown();
}
// LISTENERS

startButton.addEventListener('click', startGame)

setInterval takes a callback, but you are calling the function. If you need to pass the callback function arguments you can wrap it inside an anonymous function.

I would however suggest that the callback function given to setInterval is where your countdown logic should be. You do not need a loop because setInterval will act as a loop.

  1. Create two top-level variables, one for the count and one for the id setInterval returns.

  2. Inside the onclick handler save the setInterval id and call setInterval with the callback intervalId = setInterval(countDown, 1000);

  3. Inside the countDown callback check the value of count and call clearInterval using the saved id if the count is 0, otherwise, set the textContent to count and decrement it.


If you do not want count to be a top-level variable you can create a makeCounter function that uses a closure. It has to return a function that will become the callback function given to setInterval and you must provide the starting number for the counter.

const startButton = document.getElementById("start-button");
const counter = document.getElementById("countdown");
let intervalId;

const makeCounter = (startingCount) => {
  return () => {
    if (startingCount === 0) {
      clearInterval(intervalId);
    }
    counter.textContent = startingCount;
    startingCount--;
  };
};

const beginCountDown = () => {
  intervalId = setInterval(makeCounter(3), 1000);
};

I have probably made an error, but for me this code holds on the number 3 but does not count down.

I revisited the documentation for setInterval() and have a vague sense of the intervalId value: that it’s necessary to keep track of the intervals.

When you say setInterval takes a callback do you mean that I’m including the parenthesis when I pass set interval the function as an argument?

If I’m understanding right, you’re saying I should hand setInterval a function that returns a decreasing value on each execution of the interval, correct?

So…

setInterval(function-that-returns 3 on first iteration, 2 on second, 1…, [millisecond wait time between intervals])

I think I am confused on how to get that anonymous function an initial value and then a different value on each successive iteration (unless the return value somehow becomes the next argument on the next interval’s call of the anonymous function). Am I making any sense?

Thank you for taking the time to write up a response to my initial question!

**** Update: I got the countdown working! BUT, my clearInterval call doesn’t seem to work to stop setInterval from firing

function countDown(startInt) {
    return () => {
        counter.textContent = startInt.toString();
        startInt--;
        if (startInt === 0) {
            clearInterval(intervalId);
        }
    }
}

const startGame = () => {
    startButton.style.display = 'none';
    counter.style.display = 'block';
    setInterval(countDown(3), 1000);
    
}

I see now it has something to do with setting intervalID to the return of setInterval(). I’ll try to see if I can figure out how this works. If anyone knows of any article links that may clarify many thanks!

Alas, I was mistaken in thinking Javascript would wait until setInterval was finished. I guess there is content I need to learn before I can make this program work as I had hoped.