0

Here is my JS fiddle:

https://jsfiddle.net/apasric4/xkzwqr1m/1/

My program is supposed to start a countdown timer that calculates the time remaining in days, hours, seconds, etc from today till the the date that the user picks in the input field.

While the program updates the HTML with the correct time remaining, the problem starts when I try to update countdown timer every second. It returns back NaN values for some reason. Why is that??

Here's my JS:

const input = document.querySelector('input')
let timeInterval;
let timeStop;


input.addEventListener('change', (e) => {
    e.preventDefault()
    timeStop = true;
    endTime = Date.parse(e.target.value)
    updateHTML(endTime)
})


function updateHTML(endTime) {
  let time = calculateTimeDiff(endTime)
  if (time.total <= 0) {
    timeStop = false;
  }
  for (let pro in time) {
    let el = document.querySelector(`.${pro}`)
    if (el) {
      el.innerHTML = time[pro];
    }
  }
  updateCounter();
}


function updateCounter () {
  if (timeStop) {
    timeInterval = setInterval(updateHTML, 1000);
  } else {
    clearInterval(timeInterval);
  }
}

//returning time remaining till date in object form 
function calculateTimeDiff(endTime) {
  let start = Date.now();
  let end = endTime;
  let t = (end-start);
  let seconds = Math.floor((t / 1000) % 60);
  let minutes = Math.floor((t / 1000 / 60) % 60);
  let hours = Math.floor((t / (1000 * 60 * 60)) % 24);
  let days = Math.floor(t / (1000 * 60 * 60 * 24));
  return {
    total: t,
    days: days,
    hours: hours,
    minutes: minutes,
    seconds: seconds
  }
}
FZs
  • 16,581
  • 13
  • 41
  • 50
Bob
  • 117
  • 6
  • That means you're trying to create the Date instance with a string that it cannot understand. – Pointy Sep 26 '19 at 15:05
  • Possible duplicate of [Why does Date.parse give incorrect results?](https://stackoverflow.com/questions/2587345/why-does-date-parse-give-incorrect-results) – Heretic Monkey Sep 26 '19 at 15:07
  • Also calling `setInterval()` *again* from inside the interval callback is going to cause all sorts of crazy problems. – Pointy Sep 26 '19 at 15:07
  • @Pointy could you explain how I could change my code to make sure the date instance understands what I'm putting in? – Bob Sep 26 '19 at 15:08
  • 3
    `timeInterval=setInterval(updateHTML, 1000)` . AND `function updateHTML (endTime) {` . <--- SO undefined – epascarello Sep 26 '19 at 15:08
  • I just don't understand why endTime gives me a value initially, then after 1000ms, it gives undefined? – Bob Sep 26 '19 at 15:14
  • 1
    `setInterval(updateHTML` calls `updateHTML` with no argument. `endTime` is undefined. – Heretic Monkey Sep 26 '19 at 15:16
  • @Bob the value works initially because you call it correctly with a parameter in your event listener, but without a parameter in the `setTimeout` so it works correctly for the first time, then gets called incorrectly after one second. – SeanDemps Sep 26 '19 at 16:07

4 Answers4

1

When update goes from event - all is fine

But later you use timeInterval=setInterval(updateHTML, 1000) - and updateHTML gets executed without parameter. Use real date instead and it will work

Working example:

const input = document.querySelector('input')
let timeInterval;
let timeStop;
let savedTime;

input.addEventListener('change', (e) => {
  e.preventDefault()
  timeStop = true;
  endTime = Date.parse(e.target.value)
  updateHTML(endTime)
})

function updateHTML(endTime) {
  savedTime = endTime || savedTime;
  let time = calculateTimeDiff(savedTime)
  if (time.total <= 0) {
    timeStop = false
  }
  for (let pro in time) {
    let el = document.querySelector(`.${pro}`)
    if (el) {
      el.innerHTML = time[pro]
    }
  }
  updateCounter()
}

function updateCounter() {
  if (timeStop) {
    timeInterval = setInterval(updateHTML, 1000)
  } else {
    clearInterval(timeInterval)
  }
}

//returning time remaining till date in object form 
function calculateTimeDiff(endTime) {
  let start = Date.now()
  let end = endTime
  let t = (end - start)
  let seconds = Math.floor((t / 1000) % 60);
  let minutes = Math.floor((t / 1000 / 60) % 60);
  let hours = Math.floor((t / (1000 * 60 * 60)) % 24);
  let days = Math.floor(t / (1000 * 60 * 60 * 24));
  return {
    total: t,
    days: days,
    hours: hours,
    minutes: minutes,
    seconds: seconds
  }
}
.time {
  display: inline-block;
  border-radius: 25%;
}
<!DOCTYPE html>

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="styles.css">
</head>

<body>

  <input type="date" name="endDate">
  <div class="clock">
    <div class="time"><span class="days">0</span> Days</div>
    <div class="time"><span class="hours">0</span> Hours</div>
    <div class="time"><span class="minutes">0</span> Minutes</div>
    <div class="time"><span class="seconds">0</span> Seconds</div>
  </div>


</body>

<script src="app.js"></script>

</html>
Alex Vovchuk
  • 2,828
  • 4
  • 19
  • 40
  • Why was it necessary to create that new variable savedTime? Why does that allow us not have to put a parameter in updateHTML when we call it every second? – Bob Sep 26 '19 at 15:27
  • You can do this, but you have to take it from somewhere. In your case you use date in parameter only first time when execute from event. But all timeout calls don't have an event and don't know where to take the date – Alex Vovchuk Sep 26 '19 at 15:32
  • Hey this works. But I also noticed that the countdown timer doesn't tick down by the second. It ticks down faster then that. Do you know why that is? – Bob Sep 26 '19 at 16:26
1

So your code works fine but your problem is with your endTime. In your setInterval, you are calling updateHTML without the parameter endTime so that causes an error as it doesn't have a reference for the param.

You can either simply update your updateCounter function to accept this as a parameter and pass it to your setInterval function:

function updateCounter (endTime) {
  if (timeStop) {
    timeInterval=setInterval(() => updateHTML(endTime), 1000)
  } else {
    clearInterval(timeInterval)
  }
}

and then call updateCounter with the endTime at the bottom of your updateHtml function.

Or, remove endTime as a parameter from updateHtml and make it a global variable:

const input=document.querySelector('input')
let timeInterval;
let timeStop;
let endTime;


input.addEventListener('change', (e)=> {
  e.preventDefault()
  timeStop=true;
  endTime=Date.parse(e.target.value)
  updateHTML()
})


function updateHTML () {
  let time=calculateTimeDiff(endTime)
  if (time.total<=0) {
    timeStop=false
  }
  for (let pro in time) {
    let el=document.querySelector(`.${pro}`)
    if (el) {
      el.innerHTML=time[pro]
    }
  }
  updateCounter()
}


etc...
SeanDemps
  • 200
  • 7
0

try this....

const input=document.querySelector('input')
let timeInterval;
let timeStop;
let endTime;

input.addEventListener('change', (e)=> {
  e.preventDefault()
  timeStop=true;
  endTime=Date.parse(e.target.value)
  updateHTML()
})


function updateHTML () {
  let time=calculateTimeDiff()
  if (time.total<=0) {
    timeStop=false
  }
  for (let pro in time) {
    let el=document.querySelector(`.${pro}`)
    if (el) {
      el.innerHTML=time[pro]
    }
  }
  updateCounter()
}


function updateCounter () {
  if (timeStop) {
    timeInterval=setInterval(updateHTML(), 1000)
  } else {
    clearInterval(timeInterval)
  }
}

//returning time remaining till date in object form 
function calculateTimeDiff () {
  let start=Date.now()
  let end=endTime
  let t=(end-start)
  let seconds = Math.floor((t / 1000) % 60);
  let minutes = Math.floor((t / 1000 / 60) % 60);
  let hours = Math.floor((t / (1000 * 60 * 60)) % 24);
  let days = Math.floor(t / (1000 * 60 * 60 * 24));
  return {
    total: t,
    days: days,
    hours: hours,
    minutes: minutes,
    seconds: seconds
  }
}
Abhinav
  • 1,202
  • 1
  • 8
  • 12
  • Hey I think the code didn't get formatted properly? – Bob Sep 26 '19 at 15:19
  • Hey why does it say maximum call stack exceeded for my code? – Bob Sep 26 '19 at 15:24
  • sry, try with not sending endTime as parameter to any function as its global – Abhinav Sep 26 '19 at 15:27
  • Sorry, I don't understand what you mean? Do you want me to not set endTime as global? Could you update your code so I can what you are trying to say? – Bob Sep 26 '19 at 15:29
  • Hey this works. But I also noticed that the countdown timer doesn't tick down by the second. It ticks down faster then that. Do you know why that is? – Bob Sep 26 '19 at 16:25
0

Just add preventDefault() in function updateCounter() in your code. Hope it will work. I tried and it worked successfully.