ayyelmayo77
ayyelmayo77

Reputation: 97

Countdown using vanilla JavaScript

I have this web page with a textarea and 3 buttons. The textarea receives a number. The first button starts the coutdown from the number to 0, with a delayed output (one number per second, so if N is 10 it'll take 10 seconds). The second button pauses the countdown, and the third button resumes it (without starting over). Pressing the first button at any time during the execution restarts the coutdown with whatever number is in the textarea. This is the code I have so far:

<!DOCTYPE html>
<html>
    <body>
        <h2>Insert a number:</h2>
        <textarea id="input"></textarea>
        <br/>
        <button id="start" onclick="begin()">BEGIN</button>
        <button id="pause" onclick="pause()">PAUSE</button>
        <button id="resume" onclick="resume()">RESUME</button>
        <h1 id="result"></h1>
        <script>
            var isCounting=true,
                input=document.getElementById("input"),
                countSec;
            function begin() {
                countSec=input.value;
                if (isNaN(countSec)) alert("NaN");
                count();
            }
            function pause() {
                isCounting=false;
            }
            function resume() {
                isCounting=true;
                count();
            }
            function count() {
                var i,
                    bck=countSec;
                for (i=0; i<=bck; i++) {
                    document.getElementById("result").innerHTML=countSec;
                    countSec--;
                }
            }
        </script>
    </body>
</html>

Is there a way to stop the execution for 1 second after countSec--? I've been trying for 2 hours to do something with Date objects and setTimeout but I just can't figure out how to pause after every for iteration

Upvotes: 2

Views: 1306

Answers (7)

Paolo
Paolo

Reputation: 15827

Here is an option:

    <script>
        var input=document.getElementById("input"),
            countSec,
            timer = null;

        function begin() {
            countSec=input.value;
            if (isNaN(countSec)) alert("NaN");
            if( timer === null ) {
                timer = setTimeout( count, 1000 );
            }
        }

        function pause() {
            clearTimeout( timer );
            timer = null;
        }

        function resume() {
            if( timer === null ) {
                timer = setTimeout( count, 1000 );
            }
        }

        function count() {
            if( countSec > 0 ) {
                countSec--;
            }

            document.getElementById("result").innerHTML = countSec;

            if( countSec > 0 ) {
                timer = setTimeout( count, 1000 );
            }
        }
    </script>

As begin is called it sets a timer on count after 1 second.

When later count is invoked it decreases the counter, updates the html and schedules itself to be recalled after 1 second with setTimeout() (unless -of course- the counter reached zero)

The scheduled task is stored into timer so it can be canceled with clearTimeout() (see pause())

To resume the countdown just call count() again.

timer is set to null when the counter is not running and is checked before starting it to ensure only one counter is running.

Upvotes: 1

Omri
Omri

Reputation: 278

What you want is for your count function to contain a self invoking timer, like this:

var timer;

function count() {
    var result = document.getElementById("result");

    step();

    function step() {
        result.innerHTML=countSec;
        countSec--;
        if (countSec > 0) {
            timer = setTimeout(step, 1000);
        }
    }
}

But know that the timeout doesn't really count 1000ms. If you want to be accurate you'll have to compare to the time where it all began. and lower the interval:

var timer,
    start,
    moment,
    input = document.getElementById("input"),
    result = document.getElementById("result");

function begin() {
    var from = ((parseInt(input.value) + 1) * 1000);
    start = Date.now() + from;
    setTimeout(count, 40);
}

function pause() {
    clearTimeout(timer);
}

function resume() {
    start = Date.now() + (moment * 1000);
    count();
}

function count() {
    moment = parseInt((start - Date.now()) / 1000);
    result.innerHTML = moment;
    if (moment > 0) {
        timer = setTimeout(count, 200);
    }
}

Upvotes: 1

Andres Zapata
Andres Zapata

Reputation: 1930

Heres another solution

<!DOCTYPE html>
<html>

  <body>
    <h2>Insert a number:</h2>
    <textarea id="input"></textarea>
    <br/>
    <button id="start" onclick="begin()">BEGIN</button>
    <button id="pause" onclick="pause()">PAUSE</button>
    <button id="resume" onclick="resume()">RESUME</button>
    <h1 id="result"></h1>
    <script>
      let isCounting = true;
      let input = document.getElementById("input");
      let countSec = 0;
      let countDownInterval = false;

      function begin() {
        countSec = input.value;
        if (isNaN(countSec)) alert("NaN");
        count();
      }

      function pause() {
        isCounting = false;
      }

      function resume() {
        isCounting = true;
      }

      function count() {
        // Let's check we're doing a restart 
        if (countDownInterval) {
          clearInterval(countDownInterval);
        }

        countDownInterval = setInterval(function() {
          if (isCounting) {
            document.getElementById("result").innerHTML = countSec;
            countSec -= 1;
            // Times up ??
            if (countSec == -1) {
              clearInterval(countDownInterval);
              countDownInterval = null;
            }
          }
        }, 1000);
      }

    </script>
  </body>

</html>

jsfiddle

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386578

You could use setInterval, an intervalID and clearInterval, when the count reaches zero.

var isCounting = true,
    input = document.getElementById("input"),
    countSec,
    intervalID;

function begin() {
    countSec = +input.value;
    if (isNaN(countSec)) {
        alert("NaN");
        return;
    }
    document.getElementById("result").innerHTML = countSec;
    intervalID = setInterval(count, 1000);
}

function pause() {
    isCounting = false;
}

function resume() {
    isCounting = true;
}

function count() {
    if (countSec === 0) {
        clearInterval(intervalID);
        return;
    }
    if (isCounting) {
        countSec--;
        document.getElementById("result").innerHTML = countSec;
    }
}
<h2>Insert a number:</h2>
<textarea id="input"></textarea><br/>
<button id="start" onclick="begin()">BEGIN</button>
<button id="pause" onclick="pause()">PAUSE</button>
<button id="resume" onclick="resume()">RESUME</button>
<h1 id="result"></h1>

Upvotes: 0

Sushanth --
Sushanth --

Reputation: 55740

I think most of the solutions have already answered your question. It is a better idea to involve interval operations in your solution as that is more apt and JS already provides it.

  • Also a better idea to separate out your JS and HTML. (By avoiding inline event handlers)

HTML

<h2>Insert a number:</h2>
<textarea id="input"></textarea>
<br/>
<button id="start">BEGIN</button>
<button id="pause">PAUSE</button>
<button id="resume">RESUME</button>
<h1 id="result"></h1>

JS

// Bind the click events
document.getElementById('start').addEventListener('click', begin);
document.getElementById('pause').addEventListener('click', pause);
document.getElementById('resume').addEventListener('click', resume);

var input = document.getElementById("input");
var countSec;
var timer;

function begin() {
  debugger;
  countSec = input.value;
  if (isNaN(countSec)) alert("NaN");
  count();
  enableTimer();
}

function pause() {
  clearInterval(timer);
}

function resume() {
  enableTimer();
}

function count() {
  if (countSec >= 0) {
    document.getElementById("result").innerHTML = countSec;
  } else {
    clearInterval(timer);
  }
  countSec--;
}

// enable the timer again 
function enableTimer() {
   timer = setInterval(function() {
      count();
    }, 1000);
}

JsFiddle

Upvotes: 1

TheJim01
TheJim01

Reputation: 8876

This sounds like the perfect scenario to implement Javascript's built-in asynchronous interval operations.

Your timer should center around setInterval ( https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval ). Basically, you'll set up your interval to fire every second, which will increment your counter.

To pause your countdown, you'll want to use clearInterval ( https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval ). You can also use the intervalID to determine whether the setInterval timer is running or not.

These building blocks should get you going. If you require more information, just ask.

Upvotes: 1

Zack Tanner
Zack Tanner

Reputation: 2590

If you need a delay between each subtraction of countSec, then I recommend you use the setInterval function. I've modified your code as follows:

var isCounting=true,
    input=document.getElementById("input"),
    countSec
    interval;
function begin() {
    countSec=input.value;
    if (isNaN(countSec)) alert("NaN");
    count();
}
function pause() {
    isCounting=false;
    clearInterval(interval);
}
function resume() {
    isCounting=true;
    count();
}
function count() {
    var i,
        bck=countSec;

    document.getElementById("result").innerHTML=bck;               

    interval = setInterval(function() {
        countSec--;
        document.getElementById("result").innerHTML=countSec;               
    }, 1000);
}

Specifically, I created a global interval variable, which will keep a reference to the interval. I set the interval in count(), and then I update the #result ID at every tick of the interval (which has a delay of 1000ms). To pause, I clear the interval. I left a lot of your code in tact even though some of it isn't being used (like isCounting);

Here's the code in action: https://jsfiddle.net/exex7jh0/

Upvotes: 1

Related Questions