45

I am needing to detect when a user is inactive (not clicking or typing) on the current page for more than 30 minutes.

I thinking it might be best to use event blubbling attached to the body tag and then just keep resetting a timer for 30 minutes, but I'm not exactly sure how to create this.

I have jQuery available, although I'm not sure how much of this will actually use jQuery.

Edit: I'm more needing to know if they are actively using the site, therefore clicking (changing fields or position within a field or selecting checkboxes/radios) or typing (in an input, textarea, etc). If they are in another tab or using another program, then my assumption is they are not using the site and therefore should be logged out (for security reasons).

Edit #2: So everyone is clear, this is not at all for determining if the user is logged in, authenticated or anything. Right now the server will log the user out if they don't make a page request within 30 minutes. This functionality to prevent the times when someone spends >30 minutes filling in a form and then submitting the form only to find out that they haven't been logged out. Therefore, this will be used in combination with the server site to determine if the user is inactive (not clicking or typing). Basically, the deal is that after 25 minutes of idle, they will be presented with a dialog to enter their password. If they don't within 5 minutes, the system automatically logs them out as well as the server's session is logged out (next time a page is accessed, as with most sites).

The Javascript is only used as a warning to user. If JavaScript is disabled, then they won't get the warning and (along with most of the site not working) they will be logged out next time they request a new page.

Darryl Hein
  • 142,451
  • 95
  • 218
  • 261
  • Logging people out for just changing the tabs is probably a bad idea - what if they needed to go to another tab or program to get information to enter in to your form, and come back to find out they've been logged out? You should first consider if this is completely necessary before proceeding further. – Alex S Jun 17 '09 at 20:26
  • 1
    It is completely necessary. Yes, it is annoying, but necessary because we are dealing with emergency response data (ie, gas pipeline explodes) and we need to keep the data safe and unfortunately the user's aren't always on secure machines (location wise). We can't have people sitting down and using someone else's account. – Darryl Hein Jun 17 '09 at 20:30
  • 2
    Okay, but don't forget that JS isn't reliable for most secure tasks, as almost all browsers have a mechanism to simply disable it. You'll really want to figure out a maximum time to sit on a page, then every time a page is loaded, store the timestamp somewhere, then every action server side should be checked against this before doing anything. – Alex S Jun 17 '09 at 20:34
  • That's essentially what I'm asking about. Yes, I do realize that I can't rely on JS, but because it's an internal site, user's can't login without JS enabled. Someone would have to manually post their username and password to get in. – Darryl Hein Jun 17 '09 at 20:36
  • @Shadow, Not only do most browsers provide a simple way of disabling it, but some browsers don't even support it! – Malfist Jun 17 '09 at 20:37
  • @Darryl Hein, passwords should not be simply posted, send it though SSL! Plain text is bad! – Malfist Jun 17 '09 at 20:37
  • I still strongly recommend a server-side check, as it is possible to manually post data - if it's as secure as you say. I would strongly advise against depending on any browser for any secure task, as you never know if (in your case) someone might come up with a hack to make it appear as if JS is enabled. – Alex S Jun 17 '09 at 20:41
  • 1
    Okay...yes I am sending password through SSL. LOL. I am purely asking about timing the user out and detecting if they are actively using the site. And yes, the server will also be checking to ensure they are still logged in--it's still doing the full authentication process. – Darryl Hein Jun 17 '09 at 20:44
  • I must concur with shadow. Using javascript for this is like a security guard handing customers the key to the bank vault. The only way it works is on faith. – Breton Jun 18 '09 at 03:48
  • 1
    Again, this is *purely* to determine if they are idle. It has nothing to do with the actual authentication. – Darryl Hein Jun 18 '09 at 04:07
  • This can be done [with onfocus and onblur](http://stackoverflow.com/a/512123/209139) – TRiG May 02 '12 at 13:30
  • https://stackoverflow.com/questions/1760250/how-to-tell-if-browser-tab-is-active – Bunny Jan 12 '22 at 07:05

11 Answers11

35

This is what I've come up with. It seems to work in most browsers, but I want to be sure it will work everywhere, all the time:

var timeoutTime = 1800000;
var timeoutTimer = setTimeout(ShowTimeOutWarning, timeoutTime);
$(document).ready(function() {
    $('body').bind('mousedown keydown', function(event) {
        clearTimeout(timeoutTimer);
        timeoutTimer = setTimeout(ShowTimeOutWarning, timeoutTime);
    });
});

Anyone see any problems?

Shog9
  • 156,901
  • 35
  • 231
  • 235
Darryl Hein
  • 142,451
  • 95
  • 218
  • 261
  • 4
    One little problem - you'll want to clear the existing timer before starting another one! I've edited this change in... – Shog9 Jun 17 '09 at 21:16
  • Thxs Shog9, I've often wondered this. But, would it not clear the existing timer by overwriting variable with a new timer? – Darryl Hein Jun 17 '09 at 22:28
  • 5
    No, the variable is just a timer ID, the timer itself is an internal browser structure. Storing the ID in a variable is just a simple way to identify the internal browser structure to the clearTimeout function. – Breton Jun 18 '09 at 03:51
  • 3
    Just one addition, I added the "touchstart" event to work better on touch enabled devices. i.e. $('body').bind('mousedown keydown touchstart', function(event) {.... – Glade Mellor Jun 16 '15 at 20:47
  • 5
    To add to Glade's comment, I also use mouse move, ala: `$('body').bind('mousemove mousedown keydown touchstart', function(event) {....` – Parapluie Sep 06 '18 at 22:00
22

Ifvisible.js is a crossbrowser lightweight solution that does just that. It can detect when the user switches to another tab and back to the current tab. It can also detect when the user goes idle and becomes active again. It's pretty flexible.

Fayçal
  • 578
  • 5
  • 10
user2794286
  • 220
  • 3
  • 5
19

You can watch mouse movement, but that's about the best you're going to get for indication of a user still being there without listening to the click event. But there is no way for javascript to tell if it is the active tab or if the browser is even open. (well, you could get the width and height of the browser and that'd tell you if it was minimized)

Malfist
  • 31,179
  • 61
  • 182
  • 269
  • also you can look at the keyup event – TStamper Jun 17 '09 at 20:11
  • The OP said not clicking and not typing, so there'd be no keyup event. – Malfist Jun 17 '09 at 20:12
  • 3
    what do you mean there would be no keyup event? When the user stops typing, that is the last keyup event and by having a keyup function event cant you just check to see how long ago that keyup event occured – TStamper Jun 17 '09 at 20:16
  • Right, but the user isn't going to hold the key down for 30 minutes. There would be a key up event after the last key is typed, but the user has to type to get a key up event. – Malfist Jun 17 '09 at 20:19
  • Malfist, TStamper has it right. I can detect when the last keystroke or click was and calculate from there. – Darryl Hein Jun 17 '09 at 20:23
  • You can, but a mouse movement is more reliable. The user is almost certain to move the mouse at some point while the page is active. And even then, if you want, you can check for both events. or every event there is that javascript will allow you to check for. :) – Malfist Jun 17 '09 at 20:26
  • 3
    @Malfist-exactly what I meant, to check for both, not just one. that is why I said 'also look at the keyup event' – TStamper Jun 17 '09 at 20:27
  • 3
    @TStamper: agreed. Imagine spending a half hour typing up a long letter into the "complaints" box, only to have the site log you out just as you were putting the final flourish on a paragraph-long closing insult. It'd be infuriating... – Shog9 Jun 17 '09 at 20:56
  • There is a way to detect if current tab is active: http://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active – Seppo Erviälä Jul 13 '12 at 11:29
16

I just recently did something like this, albeit using Prototype instead of JQuery, but I imagine the implementation would be roughly the same as long as JQuery supports custom events.

In a nutshell, IdleMonitor is a class that observes mouse and keyboard events (adjust accordingly for your needs). Every 30 seconds it resets the timer and broadcasts an state:idle event, unless it gets a mouse/key event, in which case it broadcasts a state:active event.

var IdleMonitor = Class.create({

    debug: false,
    idleInterval: 30000, // idle interval, in milliseconds
    active: null,
    initialize: function() {
        document.observe("mousemove", this.sendActiveSignal.bind(this));
        document.observe("keypress", this.sendActiveSignal.bind(this));
        this.timer = setTimeout(this.sendIdleSignal.bind(this), this.idleInterval);
    },

    // use this to override the default idleInterval
    useInterval: function(ii) {
        this.idleInterval = ii;
        clearTimeout(this.timer);
        this.timer = setTimeout(this.sendIdleSignal.bind(this), ii);
    },

    sendIdleSignal: function(args) {
        // console.log("state:idle");
        document.fire('state:idle');
        this.active = false;
        clearTimeout(this.timer);
    },

    sendActiveSignal: function() {
        if(!this.active){
            // console.log("state:active");
            document.fire('state:active');
            this.active = true;
            this.timer = setTimeout(this.sendIdleSignal.bind(this), this.idleInterval);
        }
    }
});

Then I just created another class that has the following somewhere in it:

Event.observe(document, 'state:idle', your-on-idle-functionality);
Event.observe(document, 'state:active', your-on-active-functionality)
yalestar
  • 9,334
  • 6
  • 39
  • 52
10

Ifvisible is a nice JS lib to check user inactivity.

ifvisible.setIdleDuration(120); // Page will become idle after 120 seconds

ifvisible.on("idle", function(){
   // do something
});
Darryl Hein
  • 142,451
  • 95
  • 218
  • 261
Jerome Anthony
  • 7,823
  • 2
  • 40
  • 31
3

Using jQuery, you can easily watch mouse movement, and use it to set a variable indicating activity to true, then using vanilla javascript, you can check this variable every 30 minutes (or any other interval) to see if its true. If it's false, run your function or whatever. Look up setTimeout and setInterval for doing the timing. You'll also probably have to run a function every minute or so to reset the variable to false.

Alex S
  • 25,241
  • 18
  • 52
  • 63
3

Here my shot:

var lastActivityDateTime = null;

function checkActivity( )
{
    var currentTime = new Date();
    var diff = (lastActivityDateTime.getTime( ) - currentTime.getTime( ));
    if ( diff >= 30*60*1000)
    {
        //user wasn't active;
        ...
    }
    setTimeout( 30*60*1000-diff, checkActivity);
}

setTimeout( 30*60*1000, checkActivity); // for first time we setup for 30 min.


// for each event define handler and inside update global timer
$( "body").live( "event_you_want_to_track", handler);

function handler()
{
   lastActivityDateTime = new Date();
   // rest of your code if needed.
}
Artem Barger
  • 40,769
  • 9
  • 59
  • 81
  • 2 questions: (1) is there a reason why you wouldn't just keep reseting the timer and (2) why use live? The body tag is never removed or added, or am I missing something? – Darryl Hein Jun 17 '09 at 20:26
  • 1. I'm reseting the timer, or I didn't understand your question. 2. Used live, just because 5 sec. before used in mine code. – Artem Barger Jun 17 '09 at 20:29
  • @Artem- live() is for looking for new dynamically created elements – TStamper Jun 17 '09 at 20:32
  • How are you resetting the timer? The setTimeout is just creating another timeout everytime the event happens. – Darryl Hein Jun 17 '09 at 20:37
  • @Darryl: checkActivity is just running periodically. the actual work of checking whether 30 minutes has passsed is done by comparing currentTime with lastActivityDateTime – Breton Jun 18 '09 at 04:25
2

If it's a security issue, doing this clientside with javascript is absolutely the wrong end of the pipe to be performing this check. The user could easily have javascript disabled: what does your application do then? What if the user closes their browser before the timeout. do they ever get logged out?

Most serverside frameworks have some kind of session timeout setting for logins. Just use that and save yourself the engineering work.

You can't rely on the assumption that people cannot log in without javascript, therefore the user has javascript. Such an assumption is no deterrent to any determined, or even modestly educated attacker.

Using javascript for this is like a security guard handing customers the key to the bank vault. The only way it works is on faith.

Please believe me when I say that using javascript in this way (and requiring javascript for logins!!) is an incredibly thick skulled way to engineer any kind of web app.

Breton
  • 15,401
  • 3
  • 59
  • 76
  • 1
    A comment though. If the application is using some sort of timed ajax requests to keep the information up to date, this may update your 'session timeout' on the server. So tracking your click events and sending this information with your ajax request parameters would make the "detection" of an idle state easier. – gnarf Jun 18 '09 at 04:02
  • 1
    Yes that's a fair comment. My main point is that the gatekeeper should be on the server, not the client. Supplimentary information from the client might be useful, but it's important to plan for failure, and not come to depend on client side code for security. – Breton Jun 18 '09 at 04:19
1

Without using JS, a simpler (and safer) way would simply be to have a lastActivity timestamp stored with the user's session and checking it on page load. Assuming you are using PHP (you can easily redo this code for another platform):

if(($_SESSION['lastAct'] + 1800) < time()) {
    unset($_SESSION);
    session_destroy();
    header('Location: session_timeout_message.php');
    exit;
}

$_SESSION['lastAct'] = time();

and add this in your page (optional, the user will be logged out regardless of if the page is refreshed or not (as he logs out on next page log)).

<meta http-equiv="refresh" content="1801;" /> 
Andrew Moore
  • 93,497
  • 30
  • 163
  • 175
  • Yes, I already have this. Remember, only a warning to help out the user so they don't loose information. This is not the authentication process for the site!! – Darryl Hein Jun 18 '09 at 04:47
  • In wish case just calculate the time since the last request (if you are not using Ajax, this is simple. If not, you'll need to update a variable everytime an ajax request is made). If no request has been made in the last, let's say 20 minutes, display a warning saying that their session expires in 10 minutes. – Andrew Moore Jun 18 '09 at 05:11
  • Refreshing the page automatically is not a good practice, as the user can be editing something and will lose the changes. You can better send an AJAX request to check the session every while. – Amr Sep 23 '21 at 17:55
1

You can add and remove classes to the document depending on the user active status.

// If the window is focused, a mouse wheel or touchmove event is detected
$(window).on('focus wheel touchmove', function() {
  $( 'html' ).addClass('active').removeClass('inactive');
});

// If the window losses focus
$(window).on('blur', function() {
  $( 'html' ).addClass('inactive').removeClass('active');
});

After that, you can check every while if the html has the "active" class and send an AJAX request to check the session status and perform the action you need:

setInterval( function() {
  if ( $( 'html' ).hasClass('active') ) {
  //Send ajax request to check the session
    $.ajax({
      //your parameters here
    });
  }
}, 60000 ); /* loops every minute */
Amr
  • 574
  • 1
  • 11
  • 15
0

If your concern is the lost of information for the user after a login timeout; another option would be to simply store all the posted information upon the opening of a new session (a new session will always be started when the older session has been closed/scrapped by the server) when the request to a page is made before re-routing to the logging page. If the user successfully login, then you can use this saved information to return the user back to where he was. This way, even if the user walk away a few hours, he can always return back to where he was after a successful login; without losing any information.

This require more work by the programmer but it's a great feature totally appreciated by the users. They especially appreciate the fact that they can fully concentrate about what they have to do without stressing out about potentially losing their information every 30 minutes or so.

SylvainL
  • 3,926
  • 3
  • 20
  • 24