erichynds

Hi, I'm Eric Hynds, a front-end website developer living outside of Boston, Massachusetts. I'm passionate about developing functional, standard-compliant, and user-friendly websites.

A New And Improved jQuery Idle Timeout Plugin

This is a followup to an older post of mine about notifying & prompting your users for action once they’ve become idle. I’ve since re-written and abstracted the logic, creating a much more useful & customizable plugin. The main issue with my first implementation, which I’ve addressed in this update, is that the server was never actually polled to maintain an active server-side session during long periods of use. The server-side session might be set to expire after 30 minutes, but a user could be active on your application without a page load for longer than that. As such, following a page load after 30 minutes, the user would (presumably) be prompted to log back in… not a great user experience.

jQuery Idle Timeout

The gist of here is that after a user is idle after a configurable period of time, a div/UI dialog/whatever opens, warning the user that his/her session is about to expire. The user has X number of seconds to click a “resume” link in order to continue their session. If this event is not fired, the user is relocated (configurable) to a page where you’d likely implement server-side code to end their session. Idle state is one without any mouse movement or keypresses, and is detected with Paul Irish’s idleTimer plugin.

Polling requests keep the server-side session alive

Polling requests are automatically sent to the server at a configurable interval, maintaining the users session while s/he is using your application for long periods of time.  My personal use case for this was an application where the user could write and send emails, but some users took so long to write their message that the server-side session expired before they submitted the form and their e-mail was lost. With ColdFusion and presumably other scripting languages, hitting a CFM file is enough to reset the internal server-side session timer.

Polling is done with a simple setInterval call so requests are not queued per se, but you are protected against failed & built-up requests. If the AJAX request exceeds your configured timeout limit, the request fails, OR the response does not match explicitly what you expect X amount of times, the script aborts itself.  This was designed to prevent long-running requests from building up and unnecessarily polling the server when the server isn’t even responding properly.  The likely scenario here is if the server goes down while the user is working on your page, or is overloaded and sporadically throwing 500′s or similar.

Usage

Here’s a quick mockup of a demo that opens a UI dialog window after the user is idle for 5 minutes. The user can choose to either keep his/her session alive, or end the session. A live demo of this can be found here. First, markup the dialog’s HTML:

<!-- dialog window markup -->
<div id="dialog" title="Your session is about to expire!">
	<p>You will be logged off in <span id="dialog-countdown"></span> seconds.</p>
	<p>Do you want to continue your session?</p>
</div>

Next, initiate the UI dialog widget:

// setup the dialog
$("#dialog").dialog({
	autoOpen: false,
	modal: true,
	width: 400,
	height: 200,
	closeOnEscape: false,
	draggable: false,
	resizable: false,
	buttons: {
		'Yes, Keep Working': function(){
			// Just close the dialog. We pass a reference to this
			// button during the init of the script, so it'll automatically
			// resume once clicked
			$(this).dialog('close');
		},
		'No, Logoff': function(){
			// fire whatever the configured onTimeout callback is.
			$.idleTimeout.options.onTimeout.call(this);
		}
	}
});

Finally, init the idleTimeout plugin. We’ll pass in the #dialog div as the first parameter, and the button that will trigger the “resume” functionality as the second parameter. In this demo, the resume logic needs to be bound to the “Yes…” button, which can be found by retrieving the :first button in the div .ui-dialog-buttonpane.

// start the plugin
$.idleTimeout('#dialog', 'div.ui-dialog-buttonpane button:first', {
	idleAfter: 300, // user is considered idle after 5 minutes of no movement
	pollingInterval: 60, // a request to keepalive.php (below) will be sent to the server every minute
	keepAliveURL: 'keepalive.php',
	serverResponseEquals: 'OK', // the response from keepalive.php must equal the text "OK"
	onTimeout: function(){
 
		// redirect the user when they timeout.
		window.location = "timeout.htm";
 
	},
	onIdle: function(){
 
		// show the dialog when the user idles
		$(this).dialog("open");
 
	},
	onCountdown: function(counter){
 
		// update the counter span inside the dialog during each second of the countdown
		$("#dialog-countdown").html(counter);
 
	},
	onResume: function(){
 
		// the dialog is closed by a button in the dialog
		// no need to do anything else
 
	}
});

Options

Parameter Description Default
warningLength The number of seconds after user is idle to show the logout warning. 30
keepAliveURL A url to call to keep the session alive while the user is active. 30
serverResponseEquals The response from keepAliveURL must equal this text. OK
idleAfter The user is considered idle after this many seconds. 10 minutes default. 600
pollingInterval A polling request will be sent to the server every X seconds. 30
failedRequests The number of failed polling requests until the script is aborted. 5
AJAXTimeout The timeout passed to $.ajax in milliseconds. 250
onResume A callback to fire when the session is resumed (by clicking the resume link). function(){}
onIdle Fires when the user becomes idle. function(){}
onCountdown Fires during each second of the warningLength parameter. function(){}
onTimeout A callback to fire when the session times out. function(){}
onAbort A callback to fire when the script is aborted due to too many failed polling requests. function(){}

Enjoy!

Related posts:

  1. Creating a Mint.com Style Idle Logout Timer Using jQuery
  2. A Recursive setTimeout Pattern
  3. jQuery: modifying the submit button when a form is submitted

Tags: , ,

  • http://twitter.com/blowsie Sam Blowes

    Great plugin Eric thanks!
    The logic behind the new updates is great.

    However I am still getting timeout issues on my site.
    I have found that no matter how often I set the “pollingInterval” it only ever requests the “keepAliveURL” page a maximum of 5 times (I have also forced my page not to be cached).

    This causes some issues… if my time out time is set to 10 minutes, and my Polling Interval is every 1 minute. I only get 5 minutes worth of requests. If I am working for 15 minutes, the server times me out.

    Probally something going wrong my end, although I have tested it on a variety of sites.
    Otherwise, its a great plugin. Thanks.

  • Jac

    Great Plugin, Thanks

    In the keepalive.php file don´t need “session_start()” or other php tag to keep the session?

    Thanks again.

  • ehynds

    Yeah, I think PHP requires session_start() to resume a session. I'm not too sure though, in ColdFusion hitting a .cfm is enough to reset the internal session counter, but may not be true with PHP.

  • Jac

    I think that the keepalive.php requires something like:

    <?php
    session_start();
    echo 'OK';
    ?>

    … and the last question:
    In my text area use the tinyMCE editor and don´t detect when my cursor is inside, any idea?

  • http://Workman.cl Jac

    I think that the keepalive.php requires something like:

    session_start();
    echo 'OK';

    … and the last question:
    In my text area use the tinyMCE editor and don´t detect when my cursor is inside, any idea?

  • dongzky

    Hi Eric, nice and helpful plugin. Just one question. Why don't we transfer the polling logic to the onResume function rather than hitting the server from time to time to keep the session alive? By this, I am assuming that the client timeout should be less than the server session timeout. If client timeout is greater than the server session timeout, I think it wouldn't make sense.

  • dongzky

    Hi Eric, nice and helpful plugin. Just one question. Why don't we transfer the polling logic to the onResume function rather than hitting the server from time to time to keep the session alive? By this, I am assuming that the client timeout should be less than the server session timeout. If client timeout is greater than the server session timeout, I think it wouldn't make sense.

  • Weasel

    I really love this code and how you did this. One problem I ran into is that it doesn't work on IE 7. On IE 7, I get an error on line 130.

  • Matrix333

    Eric – I tried implementing this on an ASP.NET site, but was not able to get the polling to work. The KeepAlive.aspx is being hit and getting the correct response, but it does not seem to refresh the session. Any ideas?

    Thanks,
    Cory

  • Anonymous

    Yeah, it might, it varies language to language. In ColdFusion, just hitting a .cfm/.cfc page is enough to reset the internal session clock… not sure about other languages though.

  • ehynds

    I just tested the demos on IE7 and they seem to be working fine. Might it be something in your implementation? Can you pastie some code?

  • ehynds

    A good example is wordpress; your backend session could be an hour, but it might take you a few hours to type up a blog post. The script doesn't get a chance to hit the onResume callback since you're actively using the page. To maintain the server-side session, we have to poll the server on some interval.

  • ehynds

    Does the onAbort callback fire?

  • http://aldoescudero.com.ar Aldo Escudero

    Thanks for this plugin. I would like to say that I have posted on my blog what I did to make it work on rails 3. http://bit.ly/cz4Dis

  • ajescudero

    Thanks for this plugin. I have published on my blog how to make it work in rails 3. Hope it helps someone. http://bit.ly/cz4Dis

  • http://twitter.com/estebistec Steven Cummings

    What license is this code publish under. For some reason I recall seeing the MIT license referenced here (or maybe in another post?), but now I'm having trouble finding it. Thanks.

  • ehynds

    MIT & GPL. Do whatever you'd like with it:)

  • http://twitter.com/estebistec Steven Cummings

    I just noticed that idletimer clears timers on user activity, but that idletimeout doesn't take the same opportunity to invoke _keepAlive and ensure the server's session is refreshed on user activity.

    I suppose this requirement may differ between apps, but I believe idletimeout should either do, or provide a hook to do something like the following around line 39:

    else if $.data(document, 'idleTimer') === 'active' { self._keepAlive(); }

  • http://twitter.com/estebistec Steven Cummings

    Thanks!

  • http://twitter.com/estebistec Steven Cummings

    err, nevermind. That didn't work the way I thought. I thought it would allow me to trigger on detected activity, outside of idletimeout. It seems to be triggered at the same time as onResume.

  • Sam Bernard

    I found a similar issue but the problem was opposite… The script continued to poll the server while the user was idle…. until the idleAfter value had been reached. That means that the session is last refreshed right before the idleAfter value is reached- meaning that it is extended far beyond the idleAfter value.

    I changed a few lines and now it works like a charm- once the idleAfter value has been reached the polling stops, and, unless the user does anything to make the idleTimer reset(like move his mouse) it waits until the idleAfter time has been reached again, and then shows the message at the top of the screen.

    $(document).bind(“idle.idleTimer”, function(){

    // if the user is idle and a countdown isn’t already running
    if( $.data(document, ‘idleTimer’) === ‘idle’ && !self.countdownOpen ){
    self._stopTimer();
    idle = window.setTimeout(function(){
    self._idle()
    self.countdownOpen = true;
    }, options.idleAfter * 1000);
    }
    });

    $(document).bind(“active.idleTimer”, function(){
    if($.data(document, ‘idleTimer’) === ‘active’ && !self.countdownOpen){
    window.clearTimeout(idle);
    self._startTimer();
    }
    });

  • http://twitter.com/estebistec Steven Cummings

    However, I think the concern is still valid. An example:

    Say your server session age is set to 20 minutes, and this timeout is of course reset every time the user changes pages or their page makes an ajax call to the server on their behalf.

    However, idletimer/timeout resets its own timer on every user activity. So if my last mouse movement was 5 minutes in, the page on the client gets another 20 while the server is still counting down from 15 min. Lets say in the last 30 seconds the user clicks “Keep working”. The keep-alive ping fails and they're redirected to login. This “works”, but it's kinda of bad UX because the countdown gave the impression of better accuracy.

    So, I believe any activity events should fire _keepAlive(). Of course I can do this myself (and will), but I thought I should bring it up as a possible option for idletimeout.

  • http://twitter.com/estebistec Steven Cummings

    Or is the polling meant to simply keep the session alive on the back-end and move the entire session duration tracking to the front-end (client/html/js)? I feel a little silly for not thinking of that sooner.

  • http://twitter.com/estebistec Steven Cummings

    It's definitely not working that way though.

  • http://twitter.com/estebistec Steven Cummings

    Note to django users: http://stackoverflow.com/questions/1366146/djan…

    This will affect your usage of jquery-idle-timeout.

  • SB

    The code is really good.. I have a one problem that i won’t be able to set it for 20 or more minute. When I tried the diolog box won’t open

  • Paul

    Great script, thanks!
    It does what I am after although I was wondering about multiple windows.
    If the user opens another window on the site I’m presuming they could get logged out by the non focus page redirecting although they are still working on the other one? Am I right or does this handle that too.
    If this problem exists then is there a way to detect activity in one of multiple windows?
    Whichever way I think around it it seems a problem but I’m not very good with jquery so might be totally missing something obvious.
    Cheers,
    Paul

  • Anonymous

    Yeah, this is certainly one limitation. You’d have to work with cookies or use the postMessage API, neither of which I’ve included (yet)

  • I’m_Lost_2

    hello

    Very good bit of code :-) we are looking to implement this in to a project if we can, unfortunatley for me our stuff is written in Progress and Escript if anyone has heard of it??

    I need to add the session to the two url’s and have no idea how to do this usual method with Escript is to do the following:
    [code]logout[/code] which when rendered in a browser will be http://www.example.com/logout.html?sessID=fdfew234dfg4

    does anyone know how to add the session inside the window.location = “logout.html”;

    Thanks in advance

  • Anonymous

    Yes, Great script. However I am new to all this. Can you explain the framework of how this ties into session cookies?

    Does it sound right by saying….
    ~ every time a page is visited, cookies are checked, and if set, session variables are set.
    ~ now user remains on one page
    ~ this script checks to see if that session has expired each time it polls the server
    ~ if session is longer than the default 1440 and session has expired, then this script runs to check if person has been idle ….and if so gives the option to click yes and run the script to reset the session…and if no, sends the person to the logout page.

    Is this right? IF so, when the session resets from the keepalive file, should the cookies be reset too?>

    Sorry, just trying to understand how all this fits together.
    thanks

  • Paul

    This works nicely, thank you. If you are running everything over https, 250 might be a little low, so if you see abort errors, up the AJAXTimeout value.

    Also, a countdown above 60 seconds kinda looks funky out of the box :-) . I’m no JS expert, but I glued up a MM:SS that will fall right into the onCountdown funtion:

    var sec = counter % 60; // set the seconds
    var min = Math.floor(counter/60); // set the minutes

    sec–;
    if (sec == -01) {
    sec = 59;
    min = min – 1;
    } else {
    min = min;
    }
    if (sec<=9) { sec = "0" + sec; }
    var time = (min<=9 ? "0" + min : min) + ": " + sec;
    if (min == '00' && sec == '00') {
    time = "Good Bye";
    }
    $("#dialog-countdown").html(time);

  • Paul

    Also, I had to hack in a randomizer for IE under SSL and other things that like to cache. There should probably be some sort of cache:false setting that adds a random number or seconds since epoch. I prefer Math.random() only because it is less code.

  • Ani

    if there is a alert() used in the js for any conformation for any other activity. Then time out idle time out get in between in chrome browser…

    is there a solution to it ?

  • Rbrandt

    Why poll so often (as in your examples)? The session on my (php) server is roughly 24 minutes. Polling every 30 seconds seems unnecessary, but I assume that there is a reason for it, just don’t know what it is. For a 24 minute session timeout it would seem like ever 15 minutes would be plenty.

  • Microgen86

    is it possible to stop the timer or reset the counting back to zero?

  • Brad

    I just started working with your script and love it.
    I have a question if you have a minute.
    I run a seperate timer on the page that calls an ajax update (user optional) and I’m just wondering if there’s a way I can call the resume function directly so that I can overide the session timer.
    I’m guessing a new function needs to be added but I have no idea where to add/edit your plugin

  • Ryan Lawrence

    Awesome script!

  • Young

    No idea why Chrome alert/confirm causes idle to be triggered, but here is my work-around. In jquery.idletimer.js, the toggleSTate function, replace the the “// reset timeout counter” with:

    // reset timeout counter
    var elapsed = (+new Date) – f.olddate;
    f.olddate = +new Date;

    // handle Chrome always triggering idle after js alert or comfirm popup
    if (idle && (elapsed < timeout)) {
    idle = false;
    return;
    }

  • Anonymous

    It’s just to demo how it works. In the real world it’s completely unnecessary.

  • Anonymous

    The point of the polling is not to see if the session has expired, but to keep the session alive. Hitting a server-side script page resets the “idle” counter in most (all, maybe?) server-side languages. This way, the user could be active on a single page of your site longer than the session timeout.

  • Anonymous

    Hey there,

    I just pushed version 1.1 to github which allows you to access the object directly. This should do it:

    $('#element').data('idletimeout')._onResume();

  • Young

    Slight adjustment to fix an issue where the idletimeout timer may not start if there is no activity after the page loads:

    // reset timeout counter
    var elapsed = (+new Date) – f.olddate;
    f.olddate = +new Date;

    // handle Chrome always triggering idle after js alert or comfirm popup
    if (idle && (elapsed < timeout)) {
    idle = false;
    clearTimeout($.idleTimer.tId);
    if (enabled)
    $.idleTimer.tId = setTimeout(toggleIdleState, timeout);
    return;
    }

  • Young

    Slight adjustment to fix an issue where the idletimeout timer may not start if there is no activity after the page loads:

    // reset timeout counter
    var elapsed = (+new Date) – f.olddate;
    f.olddate = +new Date;

    // handle Chrome always triggering idle after js alert or comfirm popup
    if (idle && (elapsed < timeout)) {
    idle = false;
    clearTimeout($.idleTimer.tId);
    if (enabled)
    $.idleTimer.tId = setTimeout(toggleIdleState, timeout);
    return;
    }

  • Young

    FYI, in line 31, “idletimeout” is misspelled:

    $.data( elem[0], ‘idletimout’, this );

  • http://twitter.com/judsonmitchell Judson Mitchell

    Thanks for this script. Marvelous!

  • Naren Mca36

    I tried it in Window Chrome & Safari? I added a validation on page launched an alert box will cause the onIdle event to fire immediately. I haven’t found a solution to this yet. Safari &Chrome browser.

    Is there any solution?

  • Anonymous

    Whoops, thanks – fixed.

  • Anonymous

    I’m not sure why this happens but haven’t really investigated it yet; Young has a work around: http://www.erichynds.com/jquery/a-new-and-improved-jquery-idle-timeout-plugin/#comment-141733869

  • http://pulse.yahoo.com/_TMIWXZDUDZSCUIST3RMFLFTFKY webchetan

    I am having issues when I use the timeout control with another 3rd party ajax control. I am getting error right at the begining of the call $.idleTimeout. When I load the page it gives me an error stating “Object doesn’t support this property or method” Any help….?

  • Beowshawitz

    So what would be the best way to reset the timer from a child page?

  • Anonymous

    I’m unable to reproduce this in IE or Chrome. As long as my mouse is still the timer kicks in after 5 seconds, focused or unfocused.

  • Rick

    It must be environmental. When I was trying to solve this a week or two ago, I ended up concluding the underlying idletimer plugin by Paul Irish was the problem. It seems that the mousemove event was firing in IE with no mouse movement. Since we really didn’t even want to keep alive the user session with mouse movement, I removed that from his list of events, and we were on our way.

    Today, now, I get the disqus notice that you had posted, and go back to Paul’s demo and this demo and can no longer reproduce my problem on either page! (Argh!) The strange thing is, it wasn’t just me, the entire QA team here had the problem, which is how I was assigned the defect for our usage of this. The only thing I can think of is that our IT guys had something on our machines. (I have not yet confirmed the demo here and at Paul’s site now work for our QA.)

  • http://profiles.google.com/ask21900 David Goodman

    First let me say that this is a great script! Thank you for providing it to the masses.

    I am hoping for a little help with a modification I am trying to make. My modified code is below. Obviously, the alert is just for testing purposes. It triggers fine. The $.post has been fully tested and proven. I also have a error handler that doesn’t show any error occurring. The issue I am having is that the ajax call doesn’t trigger. In case it isn’t obvious, my goal is to save the note form before logout (if the user is on a certain page).

    onTimeout: function()
    {
    // redirect the user when they timeout.

    if(document.location.href.indexOf(‘addnote.php’) > -1)
    {
    alert($(“#noteForm”).serialize());
    $.post(“ajax.php”, $(“#noteForm”).serialize());
    }
    window.location = “logout.php”;
    },

    Any help with this addition would be greatly appreciated!!

  • http://profiles.google.com/ask21900 David Goodman

    I figured out a work-around. Although it doesn’t autosave before redirecting to logout, I just moved my condition down into the timer’s _keepAlive method. This allows me to auto-save on each keepAlive call when on that page.

  • bg

    Downloaded your example and have been it unmodified and looking server logs to see if it is hitting the keepalive.php page when a user hits “Yes, Keep Working”. On Chrome this works great however on IE9 and Safari (iphone/ipad) it doesn’t hit the keepalive.php page or it hits it one time but not again.

  • http://profiles.google.com/ian.osullivan Ian O’Sullivan

    Hi Eric
    This is a fantastic plugin. Really appreciate you sharing it.
    Regards
    Ian.

  • Anonymous

    With idletimer 0.9.100511 and Chrome 12.0.742.60 I found I had to make some changes:

    // reset timeout countervar elapsed = (+new Date) – obj.olddate;
    obj.olddate = +new Date;

    // handle Chrome always triggering idle after js alert or comfirm popup
    if (obj.idle && (elapsed < obj.timeout)) {
    obj.idle = false;
    clearTimeout($.idleTimer.tId);
    if (obj.enabled)
    $.idleTimer.tId = setTimeout(toggleIdleState, obj.timeout);
    return;
    }

  • Gmaps Try

    Where can idletimer 0.9.100511 be found?

    Is it available for download?

    I do have the same problem with Chrome when showing a standard alert window.

  • Chris

    Dave do you think you could share those changes. Its exactly what Ive been trying to accomplish.

  • Denis Valeev

    The problem you described is reproducible on my machine. Running Chrome 12.0.742.91 beta-m.

  • Ryan Arneson

    I’ve noticed that print preview in Chrome triggers the warning div. Has anybody else seen this or fixed it?

  • Dave

    Is the 30 seconds configurable in the countdown? I want the popup to appear after 25 minutes then the countdown to last 5 minutes

  • Rajesh Soni

    Hi there,

    Great job, Eric!
    I have a small problem using this plugin on a page that has iFrames.
    This plugin does NOT seem to detect “activity” on the iFrames.

    Can you please check:  http://173.204.34.73/?page_id=28

    Would appreciate any help.
    Thanks.

  • Carlos Eduardo Alvarado Pedrer

    Great Plugin! Thanks a lot