1

I've started to learn JavaScript, and I'm coding a program that get a number from the user and counts down to zero with a delay of one second for each number.

This is my code:

function DescreasNo(){
    var MyInput = parseInt(document.getElementById('HoursOfWork').value);
 var output = document.getElementById('output01');
 output.innerHTML = '';
 for ( var i=MyInput ; i>0 ; i--){
        output.innerHTML += i +"<br>";
    } 
}
<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="StyleSheet.css" />
    <script src="Script.js"></script>


    <title>EyeProctect Project</title>
</head>
<body>
 <h1>Eye Protect</h1>
    <h4>Keep Your Eyes safe</h4>
    <input type="text"  id="HoursOfWork" placeholder="Enter your hours of work ...." />
    <button class="start" onclick="DescreasNo()" >Let's Go!</button>
    <p id="output01"></p>

   
</body>
</html>

I used setTimeout and setInterval, but my problem is that it just shows zeros for each number, like this:

0, 0, 0, 0

Please help me to solve this problem.

A J
  • 3,970
  • 14
  • 38
  • 53
iMohammad
  • 83
  • 1
  • 8

4 Answers4

1

You can use setTimeout() with IIFE:

function DescreasNo(){
  var MyInput = parseInt(document.getElementById('HoursOfWork').value);
  var output = document.getElementById('output01');
  output.innerHTML = '';

  (function loop (i) {          
    setTimeout(function () {   
      output.innerHTML += i +"<br>";            
      if (--i) loop(i); // call the function until end
    }, 1000); // 1 second delay
  })(MyInput);
}
<h1>Eye Protect</h1>
<h4>Keep Your Eyes safe</h4>
<input type="text"  id="HoursOfWork" placeholder="Enter your hours of work ...." />
<button class="start" onclick="DescreasNo()" >Let's Go!</button>
<p id="output01"></p>
Mamun
  • 66,969
  • 9
  • 47
  • 59
  • Is there an advantage of this over just using `setInterval`. Seems like this will wind a lot of recursive calls onto the stack. – Mark Sep 13 '18 at 05:18
  • The benefit (in the case of my solution) is that you can have it run the first time without delay. I'm not aware of any difference in performance, but there may be. – dtbarne Sep 13 '18 at 05:24
1

You're probably misunderstanding how to user a closure along with setTimeout (or setInterval).

function decreaseNumber() {
    const total_hours = parseInt(document.getElementById('HoursOfWork').value);
    const output_div  = document.getElementById('output01');
    let current_hour  = total_hours;

    const countdown = () => {
        output_div.innerHTML += current_hour + "<br />";

        if (--current_hour > 0) {
            setTimeout(countdown, 1000); // 1000 milliseconds
        }
    };

    countdown();
}
<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
        <link rel="stylesheet" href="StyleSheet.css" />
        <script src="Script.js"></script>
        <title>EyeProctect Project</title>
    </head>
    <body>
        <h1>Eye Protect</h1>
        <h4>Keep Your Eyes safe</h4>
        <input id="HoursOfWork" placeholder="Enter your hours of work ...." />
        <button class="start" onclick="decreaseNumber()">Let's Go!</button>
        <p id="output01"></p>
    </body>
</html>
dtbarne
  • 8,110
  • 5
  • 43
  • 49
1

With setInterval you could do it like this.

function DescreasNo(){
  var MyInput = parseInt(document.getElementById('HoursOfWork').value);
  var output = document.getElementById('output01');
  output.innerHTML = '';

  var countDown = MyInput;
  var intervalId = setInterval(function () {   
      output.innerHTML += countDown +"<br>";            
      if (--countDown <= 0) 
        clearInterval(intervalId); // clear timer when finished
    }, 1000); // 1 second delay between decrements
}
<h1>Eye Protect</h1>
<h4>Keep Your Eyes safe</h4>
<input type="text"  id="HoursOfWork" placeholder="Enter your hours of work ...." />
<button class="start" onclick="DescreasNo()" >Let's Go!</button>
<p id="output01"></p>
teroi
  • 1,087
  • 10
  • 19
0

I would do this with a setInterval you can allow fractional hours if you use parseFloat instead of parseInt. You can also format the seconds fairly easily to give a nice readout.

You should be careful about clearing the interval too incase someone presses the button more than once during the countdown otherwise you will get multiple timers. Here if you press twice it resets it.

Some improvements would include validating input to make sure it's a number:

let int;
function DescreasNo() {
    clearInterval(int)  // clear interval to allow button to reset counter
    var MyInput = document.getElementById('HoursOfWork').value;
    let seconds = (parseFloat(MyInput) * 60 * 60)
    var output = document.getElementById('output01');
  
    int = setInterval(() => {
      if (seconds <= 0) {  // finished
        clearInterval(int)
        return
      }
      output.innerHTML = formatTime(seconds--)
    }, 1000)
  }
  
  function formatTime(seconds) {
    let hours = Math.floor(seconds / (60 * 60)).toString().padStart(2, '0')
    let minutes = Math.floor((seconds - hours * 3600) / 60).toString().padStart(2, '0');
    let second = Math.floor(seconds - (hours * 3600) - (minutes * 60)).toString().padStart(2, '0');
    return `${hours}:${minutes}:${second}`;
  }
<h1>Eye Protect</h1>
  <h4>Keep Your Eyes safe</h4>
  <input type="text" id="HoursOfWork" placeholder="Enter your hours of work ...." />
  <button class="start" onclick="DescreasNo()">Let's Go!</button>
  <p id="output01"></p>
Mark
  • 90,562
  • 7
  • 108
  • 148