15

I want to call a js function when there is no activity from user on the web page for specified amount of time. If there is activity from user then reset timeout. I tried to search but couldn't find anything in particular. I am familiar with setTimeout() and clearTimeout() and how they work. What I am looking for is where/how to monitor for user activity. Is there any event in which I can set and clear timer?

Thank you.

Edit #1:

This webpage has one input text box & one button. It's kind of regular chat page. When I say no user activity, I mean that the user has not typed anything in text box or has not pressed any button for specified amount of time. And one more thing that it is targeted for touch based smartphone devices.

Edit #2:

Thank you everyone for suggestions. I've implemented solution based on more than one answers provided. So I will give upvote to all answers that I've found helpful instead of accepting one as answer.

indusBull
  • 1,834
  • 5
  • 27
  • 39

7 Answers7

28
// Using jQuery (but could use pure JS with cross-browser event handlers):
var idleSeconds = 30;

$(function(){
  var idleTimer;
  function resetTimer(){
    clearTimeout(idleTimer);
    idleTimer = setTimeout(whenUserIdle,idleSeconds*1000);
  }
  $(document.body).bind('mousemove keydown click',resetTimer); //space separated events list that we want to monitor
  resetTimer(); // Start the timer when the page loads
});

function whenUserIdle(){
  //...
}

Edit: Not using jQuery for whatever reason? Here's some (untested) code that should be cross-browser clean (to a point; doesn't work on IE5 Mac, for example ;):

attachEvent(window,'load',function(){
  var idleSeconds = 30;
  var idleTimer;
  function resetTimer(){
    clearTimeout(idleTimer);
    idleTimer = setTimeout(whenUserIdle,idleSeconds*1000);
  }
  attachEvent(document.body,'mousemove',resetTimer);
  attachEvent(document.body,'keydown',resetTimer);
  attachEvent(document.body,'click',resetTimer);

  resetTimer(); // Start the timer when the page loads
});

function whenUserIdle(){
  //...
}

function attachEvent(obj,evt,fnc,useCapture){
  if (obj.addEventListener){
    obj.addEventListener(evt,fnc,!!useCapture);
    return true;
  } else if (obj.attachEvent){
    return obj.attachEvent("on"+evt,fnc);
  }
} 
Mohit Bhardwaj
  • 9,650
  • 3
  • 37
  • 64
Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • Thanks for quick answer, but I'm looking for pure JS. – indusBull Aug 15 '11 at 22:37
  • This was very helpful, thanks! In the jquery version, for me the comma-delimited list of events did not work. I had to use spaces. – Brian MacKay Apr 09 '15 at 18:32
  • 1
    You are adorable! You've just put an end to my 2-day fruitless research. I am working on a canvas image slider with createJS, and wanted an "autoslide" function when the user is not doing anything, and then halt the "autoslide" when the user clicks on something. This works perfect for me! Here's one vote up for you. – ITWitch Dec 30 '16 at 06:33
10

This calls for a debouncer:

function debounce(callback, timeout, _this) {
    var timer;
    return function(e) {
        var _that = this;
        if (timer)
            clearTimeout(timer);
        timer = setTimeout(function() { 
            callback.call(_this || _that, e);
        }, timeout);
    }
}

Used like this:

// we'll attach the function created by "debounce" to each of the target
// user input events; this function only fires once 2 seconds have passed
// with no additional input; it can be attached to any number of desired
// events
var userAction = debounce(function(e) {
    console.log("silence");
}, 2000);

document.addEventListener("mousemove", userAction, false);
document.addEventListener("click", userAction, false);
document.addEventListener("scroll", userAction, false);

The first user action (mousemove, click, or scroll) kicks off a function (attached to a timer) that resets each time another user action occurs. The primary callback does not fire until the specified amount of time has passed with no actions.

Note that no global flags or timeout variables are needed. The global scope receives only your debounced callback. Beware of solutions that require maintenance of global state; they're going to be difficult to reason about in the context of a larger application.

Note also that this solution is entirely general. Beware of solutions that apply only to your extremely narrow use case.

Wayne
  • 59,728
  • 15
  • 131
  • 126
5

Most JavaScript events bubble, so you could do something like the following:

  • Come up with a list of all the events you'd consider to be "activity from the user" (e.g., click, mousemove, keydown, etc.)
  • Attach one function as an event listener for all of those events to document (or maybe document.body for some of them; I can't remember if that's an issue or not).
  • When the listener is triggered, have it reset the timer with clearTimeout/setTimeout

So you'd end up with something like this:

var events = ['click', 'mousemove', 'keydown'],
    i = events.length,
    timer,
    delay = 10000,
    logout = function () {
        // do whatever it is you want to do
        // after a period of inactivity
    },
    reset = function () {
        clearTimeout(timer);
        timer = setTimeout(logout, 10000);
    };

while (i) {
    i -= 1;
    document.addEventListener(events[i], reset, false);
}
reset();

Note that there are some issues you'd have to work out with the above code:

  • It's not cross-browser compatible. It only uses addEventListener, so it won't work in IE6-8
  • It pollutes the global namespace. It creates a lot of excess variables that might conflict with other scripts.

It's more to give you an idea of what you could do.

And now there are four other answers, but I've already typed it all up, so there :P

sdleihssirhc
  • 42,000
  • 6
  • 53
  • 67
3

You want to monitor events like mousemove, keypress, keydown, and/or click at the document level.

Edit: This being a smartphone app changes what events you want to listen for. Given your textbox and button requirements, I'd listen to oninput and then add the resetTimeout() call to the click handler for your button.

var inactivityTimeout = 0;
function resetTimeout() {
    clearTimeout(inactivityTimeout);
    inactivityTimeout = setTimeout(inactive, 300000);
}
function inactive() {
   ...
}
document.getElementById("chatInput").oninput = resetTimeout;
gilly3
  • 87,962
  • 25
  • 144
  • 176
  • 1
    Note that this will never call `inactive` if the page loads and the user never interacts with the page. You need to call `resetTimeout()` manually. – Phrogz Aug 15 '11 at 22:36
2

Something like this:

function onInactive(ms, cb){   

    var wait = setTimeout(cb, ms); 

    // Bind all events you consider as activity
    // Note that binding this way overrides any previous events bound the same wa
    // So if you already have events bound to document, use AddEventListener and AttachEvent instead
    document.onmousemove = document.mousedown = document.mouseup = document.onkeydown = document.onkeyup = document.focus = function(){
        clearTimeout(wait);
        wait = setTimeout(cb, ms);

    };

}

IE: http://jsfiddle.net/acNfy/ Activity in the bottom right frame will delay the callback.

Paul
  • 139,544
  • 27
  • 275
  • 264
1

I'm using a nifty little 'delay' method for this that I found in this thread

var delay = (function(){
  var timer = 0;
  return function(callback, ms){
    clearTimeout (timer);
    timer = setTimeout(callback, ms);
  };
})();

use like

delay(function(){ doSomethingWhenNoInputFor400ms(); },400);
Community
  • 1
  • 1
Flion
  • 10,468
  • 13
  • 48
  • 68
0

Also, take a look at jQuery idleTimer plugin from Paul Irish (jquery.idle-timer.js). It was based on Nicholas C. Zakas' Detecting if the user is idle with JavaScript and YUI 3 article (idle-timer.js).

It looks at similar events to the other answers, plus a few more.

events  = 'mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove';
// activity is one of these events
Kevin Hakanson
  • 41,386
  • 23
  • 126
  • 155