132

I try to make a page to go to the startpage after eg. 10sec of inactivity (user not clicking anywhere). I use jQuery for the rest but the set/clear in my test function are pure javascript.

In my frustation I ended up with something like this function that I hoped I could call on any click on the page. The timer starts fine, but is not reset on a click. If the function is called 5 times within the first 10 seconds, then 5 alerts will apear... no clearTimeout...

function endAndStartTimer() {
    window.clearTimeout(timer);
    var timer;
    //var millisecBeforeRedirect = 10000; 
    timer = window.setTimeout(function(){alert('Hello!');},10000); 
}

Any one got some lines of code that will do the trick? - on any click stop, reset and start the timer. - When timer hits eg. 10sec do something.

Mark Fox
  • 8,694
  • 9
  • 53
  • 75
Tillebeck
  • 3,493
  • 7
  • 42
  • 63

8 Answers8

270

You need to declare timer outside the function. Otherwise, you get a brand new variable on each function invocation.

var timer;
function endAndStartTimer() {
  window.clearTimeout(timer);
  //var millisecBeforeRedirect = 10000; 
  timer = window.setTimeout(function(){alert('Hello!');},10000); 
}
Ankur Soni
  • 5,725
  • 5
  • 50
  • 81
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 1
    plus 1 one because it is necessary, in this case, to clear the variable that hoods the timeOut before call the time out, as so, avoiding it to be invoked twice – Diego Favero May 01 '20 at 15:30
50

The problem is that the timer variable is local, and its value is lost after each function call.

You need to persist it, you can put it outside the function, or if you don't want to expose the variable as global, you can store it in a closure, e.g.:

var endAndStartTimer = (function () {
  var timer; // variable persisted here
  return function () {
    window.clearTimeout(timer);
    //var millisecBeforeRedirect = 10000; 
    timer = window.setTimeout(function(){alert('Hello!');},10000); 
  };
})();
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
15

That's because timer is a local variable to your function.

Try creating it outside of the function.

arclight
  • 5,299
  • 1
  • 22
  • 26
13

A way to use this in react:

class Timeout extends Component {
  constructor(props){
    super(props)

    this.state = {
      timeout: null
    }

  }

  userTimeout(){
    const { timeout } = this.state;
    clearTimeout(timeout);
    this.setState({
      timeout: setTimeout(() => {this.callAPI()}, 250)
    })

  }
}

Helpful if you'd like to only call an API after the user has stopped typing for instance. The userTimeout function could be bound via onKeyUp to an input.

zero_cool
  • 3,960
  • 5
  • 39
  • 54
  • 1
    This is what I've been looking for couple hours, thanks. I was just wondering if there is a better way for this sort of result to happen? – Nikasv Nov 26 '17 at 10:33
  • 2
    @nikasv throttling, and debouncing are two alternatives: https://medium.com/@_jh3y/throttling-and-debouncing-in-javascript-b01cad5c8edf – zero_cool Nov 26 '17 at 18:30
2

Not sure if this violates some good practice coding rule but I usually come out with this one:

if(typeof __t == 'undefined')
        __t = 0;
clearTimeout(__t);
__t = setTimeout(callback, 1000);

This prevent the need to declare the timer out of the function.

EDIT: this also don't declare a new variable at each invocation, but always recycle the same.

Hope this helps.

bitsmanent
  • 154
  • 1
  • 10
1

Practical example Using Jquery for a dropdown menu ! On mouse over on #IconLoggedinUxExternal shows div#ExternalMenuLogin and set time out to hide the div#ExternalMenuLogin

On mouse over on div#ExternalMenuLogin it cancels the timeout. On mouse out on div#ExternalMenuLogin it sets the timeout.

The point here is always to invoke clearTimeout before set the timeout, as so, avoiding double calls

var ExternalMenuLoginTO;
$('#IconLoggedinUxExternal').on('mouseover mouseenter', function () {

    clearTimeout( ExternalMenuLoginTO )
    $("#ExternalMenuLogin").show()
});

$('#IconLoggedinUxExternal').on('mouseleave mouseout', function () {

    clearTimeout( ExternalMenuLoginTO )    
    ExternalMenuLoginTO = setTimeout(
        function () {

            $("#ExternalMenuLogin").hide()

        }
        ,1000
    );
    $("#ExternalMenuLogin").show()
});

$('#ExternalMenuLogin').on('mouseover mouseenter', function () {

    clearTimeout( ExternalMenuLoginTO )
});
$('#ExternalMenuLogin').on('mouseleave mouseout', function () {

    clearTimeout( ExternalMenuLoginTO )
    ExternalMenuLoginTO = setTimeout(
        function () {

            $("#ExternalMenuLogin").hide()

        }
        ,500
    );
});
Diego Favero
  • 1,969
  • 2
  • 22
  • 32
0

This works well. It's a manager I've made to handle hold events. Has events for hold, and for when you let go.

function onUserHold(element, func, hold, clearfunc) {
    //var holdTime = 0;
    var holdTimeout;

    element.addEventListener('mousedown', function(e) {
        holdTimeout = setTimeout(function() {
            func();
            clearTimeout(holdTimeout);
            holdTime = 0;
        }, hold);
        //alert('UU');
    });

    element.addEventListener('mouseup', clearTime);
    element.addEventListener('mouseout', clearTime);

    function clearTime() {
        clearTimeout(holdTimeout);
        holdTime = 0;
        if(clearfunc) {
            clearfunc();
        }
    }
}

The element parameter is the one which you hold. The func parameter fires when it holds for a number of milliseconds specified by the parameter hold. The clearfunc param is optional and if it is given, it will get fired if the user lets go or leaves the element. You can also do some work-arounds to get the features you want. Enjoy! :)

Kino Bacaltos
  • 373
  • 1
  • 2
  • 16
0

<!DOCTYPE html>
<html>

<body>

  <h2>EJEMPLO CONOMETRO CANCELABLE</h2>

  <button onclick="inicioStart()">INICIO</button>
  <input type="text" id="demostracion">
  <button onclick="finStop()">FIN</button>

  <script>
    let cuenta = 0;
    let temporalTiempo;
    let statusTime = false;


    function cronometro() {
      document.getElementById("demostracion").value = cuenta;
      cuenta++;
      temporalTiempo = setTimeout(cronometro, 500);
    }

    function inicioStart() {
      if (!Boolean(statusTime)) {
        statusTime = true;
        cronometro();
      }
    }

    function finStop() {
      clearTimeout(temporalTiempo);
      statusTime = false;

    }
  </script>

</body>

</html>
ESP
  • 11
  • 3
  • You generally try and accompany code with some sorta explanation on the site, even if it's just one sentence – Zach Jensz May 16 '22 at 01:21