Search code examples
javascriptcountdowntimer

Countdown timer won't stop looping


I've been practicing my count-down timers using Javascript and decided I wanted to try and automate it a bit. What I hope to happen is the user selects a date using a form input=date, and then that date is read with an onBlur attribute. The onBlur then activates a function to start the timer and passes the value of the calendar to the function.

My issue is not getting the timer to work. That works absolutely fine, my issue is getting it to stop or to update if a new date is chosen.

Currently what happens on expiration is the textContent is changed to "Expired" for exactly 1 tick, and then is replaced with the original countdown timer again. Similarly if the date is changed, it will flick between 2 different timers with the 2 different dates in.

I've tried using an additional function to call the countdown function. I've tried making a boolean switch to true once the timer's started to stop an update, I've tried using If statements at the end of my countdown timer, as well as the clearInterval method. So far I can't get it to stop this never-ending loop.

Any help on this would be greatly appreciated. I can see what's happening - the setInterval keeps looping, and the placeholder text has been changed. The clearInterval method doesn't appear to be stopping the timer either. Also my variable 'theTimer' which stores the countdown has a value of 5, for whatever reason. I just don't know how to resolve this at the moment. I feel it's getting a continual value feed from the calendar which keeps firing the function, just not sure.

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">

<head>
  <title>Practice</title>

</head>

<body>
  <form>
    <input type="date" id="calander-input" onblur="userDeadlineTimer(value)" /><br />

  </form>
  <h3>Time until date:</h3>
  <p id="user-deadline">0 days 0 hours 0 minutes 0 seconds</p>
  <script>
    //Javascript
    function userDeadlineTimer(value) {
      let userDeadline = new Date(value).getTime();
      //console.log("The userdeadline is " + userDeadline);
      var theTimer = setInterval(function() {
        let userReference = new Date().getTime();
        let userDelta = userDeadline - userReference;


        //Find the days

        let userDays = Math.floor((userDelta / (1000 * 60 * 60 * 24)));
        // console.log(userDays);

        //find the hours

        let userHours = Math.floor((userDelta % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
        //console.log(userHours);

        //Find the minutes

        let userMinutes = Math.floor((userDelta % (1000 * 60 * 60)) / (1000 * 60));
        //console.log(userMinutes);

        //Find the seconds

        let userSeconds = Math.floor((userDelta % (1000 * 60)) / (1000));
        //console.log(userSeconds);


        if (userDelta > 0) {
          document.getElementById("user-deadline").textContent = userDays + " days " + userHours + " hours " + userMinutes + " minutes " + userSeconds + " seconds.";
        } else {

          clearInterval(theTimer);

          document.getElementById("user-deadline").textContent = "Expired";
        }


      }, 1000);

    }
  </script>
</body>
</html>


Solution

  • Whenever your calendar-input triggers the onBlur event, it creates a new interval using setInterval. This means there are multiple intervals running in the background in parallel.

    Even though the value is updated as Expired on one of them, the other dates you have selected still run the interval callback every 1 second which can set it back to the countdown text.

    In order to solve this, you can consider declaring the variable theTimer outside of your userDeadlineTimer function. After that, call clearInterval() every time userDeadlineTimer function is called to ensure no other intervals are running in the background trying to update the same thing at once.

    An example of code changes needed for your case:

    let theTimer;
    function userDeadlineTimer(value) {
      let userDeadline = new Date(value).getTime();
      clearInterval(theTimer);
      theTimer = setInterval(function () {
        // ...
      }, 1000);
    }