31

I'm trying to pause and then play a setInterval loop.

After I have stopped the loop, the "start" button in my attempt doesn't seem to work :

input = document.getElementById("input");

function start() {
  add = setInterval("input.value++", 1000);
}
start();
<input type="number" id="input" />
<input type="button" onclick="clearInterval(add)" value="stop" />
<input type="button" onclick="start()" value="start" />

Is there a working way to do this?

double-beep
  • 5,031
  • 17
  • 33
  • 41
Web_Designer
  • 72,308
  • 93
  • 206
  • 262

8 Answers8

40

See Working Demo on jsFiddle: http://jsfiddle.net/qHL8Z/3/

$(function() {
  var timer = null,
    interval = 1000,
    value = 0;

  $("#start").click(function() {
    if (timer !== null) return;
    timer = setInterval(function() {
      $("#input").val(++value);
    }, interval);
  });

  $("#stop").click(function() {
    clearInterval(timer);
    timer = null
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="number" id="input" />
<input id="stop" type="button" value="stop" />
<input id="start" type="button" value="start" />
jessegavin
  • 74,067
  • 28
  • 136
  • 164
28

The reason you're seeing this specific problem:

JSFiddle wraps your code in a function, so start() is not defined in the global scope.

enter image description here


Moral of the story: don't use inline event bindings. Use addEventListener/attachEvent.


Other notes:

Please don't pass strings to setTimeout and setInterval. It's eval in disguise.

Use a function instead, and get cozy with var and white space:

var input = document.getElementById("input"),
  add;

function start() {
  add = setInterval(function() {
    input.value++;
  }, 1000);
}

start();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="number" id="input" />
<input type="button" onclick="clearInterval(add)" value="stop" />
<input type="button" onclick="start()" value="start" />
Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • Nice. I couldn't figure out why his Fiddle wasn't behaving. Good to know. +1 – mrtsherman Dec 16 '11 at 19:29
  • 1
    yup - jsFiddle is real neat but you have to be aware of how it works differently to a plain script file. – Alnitak Dec 16 '11 at 19:49
  • @Web_Designer just use the code in [Alnitak's answer](http://stackoverflow.com/a/8539201/139010), assuming you're actually using jQuery (as your question is tagged). – Matt Ball Dec 16 '11 at 22:27
11

As you've tagged this jQuery ...

First, put IDs on your input buttons and remove the inline handlers:

<input type="number" id="input" />
<input type="button" id="stop" value="stop"/>
<input type="button" id="start" value="start"/>

Then keep all of your state and functions encapsulated in a closure:

EDIT updated for a cleaner implementation, that also addresses @Esailija's concerns about use of setInterval().

$(function() {
    var timer = null;
    var input = document.getElementById('input');

    function tick() {
        ++input.value;
        start();        // restart the timer
    };

    function start() {  // use a one-off timer
        timer = setTimeout(tick, 1000);
    };

    function stop() {
        clearTimeout(timer);
    };

    $('#start').bind("click", start); // use .on in jQuery 1.7+
    $('#stop').bind("click", stop);

    start();  // if you want it to auto-start
});

This ensures that none of your variables leak into global scope, and can't be modified from outside.

(Updated) working demo at http://jsfiddle.net/alnitak/Q6RhG/

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • Given that this question is tagged `jquery` (which I missed) this is the best solution. – Matt Ball Dec 16 '11 at 19:36
  • 1
    for reference, the down votes here are because certain people disagreed with some of my views elsewhere, not because there's anything wrong with the answer. – Alnitak Dec 16 '11 at 20:22
  • 1
    Might want to tweak this just a tiny bit: http://jsfiddle.net/mattball/2aSAV/. Sorry about the lame downvotes. That's not how voting is meant to work. – Matt Ball Dec 16 '11 at 21:02
5

add is a local variable not a global variable try this

var add;
var input = document.getElementById("input");

function start() {
  add = setInterval("input.value++", 1000);
}
start();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="number" id="input" />
<input type="button" onclick="clearInterval(add)" value="stop" />
<input type="button" onclick="start()" value="start" />
double-beep
  • 5,031
  • 17
  • 33
  • 41
Jazz Man
  • 909
  • 7
  • 17
  • 2
    @Web_Designer - that's because of the fiddle, not because of his code. See Matt Ball's answer. Same code, but now works - http://jsfiddle.net/gEdKM/4/ – mrtsherman Dec 16 '11 at 19:28
  • Thanks @mrtsherman If i could accept a comment I'd accept yours. This is the method i'll end up using. – Web_Designer Dec 16 '11 at 20:49
  • @Web_Designer it's still using a string instead of a function in `setInterval()` – Alnitak Dec 16 '11 at 21:00
  • @Web_Designer - Alnitak is right. You should use Matt Ball's answer and not this one. I was just pointing out to downvoter that Jazz Man didn't deserve it. – mrtsherman Dec 16 '11 at 21:54
  • Could you guys provide a demo to @MДΓΓ БДLL's answer? Thanks! – Web_Designer Dec 16 '11 at 22:02
  • @Web_Designer I've updated my answer to reflect various comments and give a cleaner implementation. – Alnitak Dec 16 '11 at 22:46
3
(function(){
    var i = 0;
    function stop(){
        clearTimeout(i);
    }

    function start(){
        i = setTimeout( timed, 1000 );
    }

    function timed(){
       document.getElementById("input").value++;
       start();
    }

    window.stop = stop;
    window.start = start;
})()

http://jsfiddle.net/TE3Z2/

Esailija
  • 138,174
  • 23
  • 272
  • 326
  • 1
    because putting the functions into global scope and calling them from inline event handlers are _both_ poor practise. – Alnitak Dec 16 '11 at 19:37
  • I am down voting for the same reason Alnitak cited. – jessegavin Dec 16 '11 at 19:40
  • 1
    @Alnitak sure but setInterval is one of the worst practises ever. I am not downvoting people because they don't consider that. I am just working with the OP's code as close as possible, with the worst offenders removed. – Esailija Dec 16 '11 at 19:40
  • 1
    @Esailija IMHO for this purpose `setInterval()` is perfectly acceptable. The risk of timer events queueing up because they haven't been serviced is pretty negligible. – Alnitak Dec 16 '11 at 19:42
  • @Alnitak, right because that is the only thing bad about setInterval. sigh. How about the fact that if the elements get removed, setInterval will keep causing errors unless you do some redundant defensive code. sigh. – Esailija Dec 16 '11 at 19:44
  • @Esailija what are you on about? A repeating `setTimeout()` loop would have exactly the same _hypothetical_ problem. – Alnitak Dec 16 '11 at 19:47
  • @Alnitak No it wouldn't, it would cause one error and then stop. – Esailija Dec 16 '11 at 19:48
  • @Esailija you're picking nits in a hypothetical situation that this code doesn't have. EOD. – Alnitak Dec 16 '11 at 19:51
0

I use multiple timers this way. It isn't an elegant solution but it works well.

var timerclass = function () {
    this.initialTime = Date.now();
    var t=this;
    var Timer = setInterval(function(){t.checkTime();}, 1000); //start the timer (goes to function checkTime every 1000ms )
    this.timerstart=false;      
}; //end of constructor

timerclass.prototype.StartTheTimer = function(){    
    this.initialTime = Date.now(); //Time right now
    this.timerstart=true;
};      
    
timerclass.prototype.checkTime= function(){ 
    if(this.timerstart==true)
    {
        var timeDifference = Date.now() - this.initialTime; 
        console.log("Time ms: "+timeDifference);        
   }
};  
    
timerclass.prototype.StopTimer= function(){
    this.timerstart=false;
};      
    
module.exports = timerclass;

Then in your main code:

var MyTimerSystem = require(__dirname+'/class.timerclass.js');
var MyFirstTimerObject = new MyTimerSystem(); //First Timer 
var MySecondTimerObject = new MyTimerSystem(); //Second Timer

Stop the timer(s):

MyFirstTimerObject.StopTimer(); //Stop First Timer
MySecondTimerObject.StopTimer(); //Stop Second Timer

Restart the timer(s) from 0ms again:

MyFirstTimerObject.StartTheTimer();  //Start or restart the First timer
MySecondTimerObject.StartTheTimer(); //Start or restart the Second timer
CMP
  • 1,170
  • 12
  • 11
0

<!DOCTYPE html>
<html>

<body>

  <input type="button" onclick="mySetIntervalOff()" value="stop" />
  <input type="button" onclick="startInicio()" value="start" />

  <p id="demo"></p>

  <script>
    var mySetInterval;

    function startInicio() {
      mySetInterval = setInterval(function() {
        const date = new Date();
        document.getElementById("demo").innerHTML = date.toLocaleTimeString();
      }, 1000);
    }
    startInicio();
    clearInterval(mySetInterval);

    function mySetIntervalOff() {
      clearInterval(mySetInterval);
    }
  </script>

</body>

</html>
ESP
  • 11
  • 3
-4

You can't stop a timer function mid-execution. You can only catch it after it completes and prevent it from triggering again.

Diodeus - James MacFarlane
  • 112,730
  • 33
  • 157
  • 176