Detecting if the user is idle with JavaScript and YUI 3
Web developers have been interested in whether or not a user is idle since the Ajax explosion hit. With the introduction of more dynamic, highly interactive web interfaces came the desire to know if the user was actually doing anything at any point in time. Thus, the quest for determining if the user is idle began.
This problem has been solved, though I would argue inelegantly, in a lot of web applications: Facebook, WordPress, and Gmail all try to figure out when the user has stopped interacting with the page in order to perform some action. The usual JavaScript solution for this involves monitoring the mousemove event and, if there has been no mouse movement in a specific period of time, indicate that the user is idle. There is one major flaw in this approach and that is reliance on mouse events to indicate if the user is active or idle. This is problematic because there are, of course, two primary input devices (keyboard and mouse) attached to a computer so you’re losing 50% of the picture. If a user is typing a long email or blog post, does that mean they are idle simply because they haven’t moved the mouse? Of course not. What about those users who aren’t capable of using a mouse due to a disability, are they always idle? Once again, the answer is no.
With this background in mind, I set out to create an idle timer in JavaScript that is fitting of the complex web applications that might want to use it. I built this implementation on top of YUI 3 because it has, in a short period of time, become my favorite JavaScript library. The features I wanted to implement were:
- Allow the idle timer to be started and stopped for proper cleanup of resources.
- Fire an event when the user becomes idle.
- Fire an event when the user becomes active after having been idle.
- Provide a function so I could determine, at any point in time, if the user is idle.
These features led me to a basic interface:
Y.IdleTimer = {
isRunning: function(){
},
isIdle: function(){
},
start: function(newTimeout){
},
stop: function(){
}
};
I decided to use the YUI 3 Event utility to provide custom event support for this implementation. This is done by augmenting the Y.IdleTimer object with Y.Event.Target:
//inherit event functionality
Y.augment(Y.IdleTimer, Y.Event.Target);
This line adds basic event methods, such as fire(), subscribe(), and unsubscribe(). Using Y.Event.Target, the creation and management of custom event objects are done for you, freeing you up to focus on implementation details.
Next, I created a couple of flags: idle, which indicates if the user is idle, and enabled, which is indicates if the timer is running. These are used internally to manage the state of the timer and are returned in isIdle() and isRunning(), respectively. I also created tId, which is a place to store the timer ID when using setTimeout() and timeout, which indicates the default amount of time to wait before declaring the user idle (set to 30,000ms initially, this can be overidden by passing a value into start()).
To manage the user’s idle state, you need to attach an event handler for both mousemove and keydown. Since both of these methods bubble, I can attach the handler at the document level to manage the entire page (of course, this presupposes that no one stops bubbling before it reaches the document level). The event handler should be the same for both events so there’s no duplication of code and the handler will have to manage the timeout process. I ended up creating two functions for this purpose:
//event handler
function handleUserEvent(){
//clear any existing timeout
clearTimeout(tId);
//if the idle timer is enabled
if (enabled){
//if it's idle, that means the user is no longer idle
if (idle){
toggleIdleState();
}
//set a new timeout
tId = setTimeout(toggleIdleState, timeout);
}
}
//helper to fire events
function toggleIdleState(){
//toggle the state
idle = !idle;
//fire appropriate event
Y.IdleTimer.fire(idle ? "idle" : "active");
}
The first function handleUserEvent() is assigned to be the event handler for mousemove and keydown. It doesn’t actually use the event object for anything, so I didn’t bother defining it as an argument. Whenever the user does something, the last timer should be cleared and then an appropriate action should be taken. If the timer is stopped, then nothing happens; if it’s running, then the action is determined based on the user’s current idle state. If the user is idle, then toggleIdleState() state is called immediately to indicate that the user is not active. Then, a timer is used to delay calling toggleIdleState() because the next toggle would be back to idle.
The toggleIdleState() function simply toggles the idle flag and then fires an appropriate event. If the user is idle after the toggle, then “idle” is fired, otherwise “active” is fired. These events end up being fired exactly when the user’s idle state has changed and only once until the state changes again.
To finish up the implementation, I just filled out the existing interface skeleton to make use of these functions:
Y.IdleTimer = {
isRunning: function(){
return enabled;
},
isIdle: function(){
return idle;
},
start: function(newTimeout){
//set to enabled
enabled = true;
//set idle to false to begin with
idle = false;
//assign a new timeout if necessary
if (typeof newTimeout == "number"){
timeout = newTimeout;
}
//assign appropriate event handlers
Y.on("mousemove", handleUserEvent, document);
Y.on("keydown", handleUserEvent, document);
//set a timeout to toggle state
tId = setTimeout(toggleIdleState, timeout);
},
stop: function(){
//set to disabled
enabled = false;
//clear any pending timeouts
clearTimeout(tId);
//detach the event handlers
Y.detach("mousemove", handleUserEvent, document);
Y.detach("keydown", handleUserEvent, document);
}
};
//inherit event functionality
Y.augment(Y.IdleTimer, Y.Event.Target);
Basic usage of the idle timer is as follows:
Y.IdleTimer.subscribe("idle", function(){
//handle when the user becomes idle
});
Y.IdleTimer.subscribe("active", function(){
//handle when the user becomes active
});
//start the timer with a default timeout of 30s
Y.IdleTimer.start();
Because of the power of YUI 3, this implementation of an idle timer is very small in size and pretty straightforward to use. You can get the full source code up on GitHub, and there’s an example to play with as well.
Update (6-June-09): Updated logic per Paul’s feedback.
Update (21-June-09):YUI 2 and generic versions of the idle timer are now available at my GitHub project.
Update (28-Oct-09): YUI 3 IdleTimer is now part of YUI Gallery (more info).
Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of Yahoo!, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
You can leave a response, or trackback from your own site.




24 Comments
Good show! I’ve wanted to capture idle-ness for a while. Might be fun to use this in combo with Andrea’s CPU usage script to estimate machine/user idle-ness.
Btw, I made a quick and dirty jQuery adaptation. Hope that’s cool.
Paul Irish on June 2nd, 2009 at 5:33 pm
Good article! I like that you started out with a basic interface. Starting off with a good design is a technique every solid programmer should follow.
I never looked into the code on Facebook, WordPress, and Gmail - but do they all monitor mousemove as the basis of detecting whether a user is idle?
Mike Lee on June 3rd, 2009 at 12:07 am
@Paul - Of course I don’t mind porting to jQuery. It’s MIT-licensed, so have at it!
@Mike - Glad you enjoyed it. I’ve not looked at the code on those sites, mostly because I have better things to do then reverse-engineer someone else’s code to figure out what’s going on. However, a quick search for this type of functionality turns up a lot of solutions based on
mousemove.Nicholas C. Zakas on June 3rd, 2009 at 12:20 am
[...] Nick Zakas wrote a script for YUI3 to handle these cases. His writeup has a great description of the architecture approach he took to the script. [...]
Paul Irish » jQuery idleTimer plugin on June 3rd, 2009 at 8:21 pm
Hey Nick,
Great post - really useful stuff. I poll quite a bit on the site I work on, so this will be useful for saving all those unnecessary network calls. Gave a try at porting the logic to dojo too.
Sean
Sean O Shea on June 4th, 2009 at 12:48 am
Hi Sean, very cool! Looks like we now have this in YUI, jQuery, and Dojo. This is the beauty of MIT-licensed code, very exciting!
Nicholas C. Zakas on June 4th, 2009 at 10:57 pm
[...] Nick Zakas wrote a script for YUI3 to handle these cases. His writeup has a great description of the architecture approach he took to the script. [...]
jQuery idleTimer plugin « Dogfeeds——IT Telescope on June 5th, 2009 at 11:05 am
[...] Irish has ported a YUI3 script to jQuery for his idleTimer plugin. The plugin detects when a user has become [...]
jQuery: » This Week in jQuery, vol. 7 on June 7th, 2009 at 8:14 pm
Btw a user found a logic bug in mine that is likely in yours as well.
On the example, wait for idle then hit a single keyboard key. The setTimeout isn’t restarted so it never goes idle again. My fix just took the setTimeout call outside of the else. Cheers.
Paul Irish on June 9th, 2009 at 2:58 pm
Thanks for the update, Paul, I’ll fix that in my version as well.
Nicholas C. Zakas on June 9th, 2009 at 10:58 pm
Good post with great thought on the issue with gmail and facebook user idle detection. Highlighting your point of detecting if user id idle or not, what if user is reading something and moving mouse around as some people have this habbit of moving mouse while reading. What if you have used keyboard and mouse click combination.
Qasim on June 12th, 2009 at 11:08 am
@Qasim - the definition of “idle” in this case refers to the user having the page open but is not actively using it. If someone is reading and moving their mouse around, then I wouldn’t consider this user to be “idle”…because they’re not. They’re still using the page. Using mouse clicks instead of mouse moves changes the definition of “active” to be more restrictive and I believe to restrictive to be useful.
Nicholas C. Zakas on June 13th, 2009 at 4:03 pm
Just out of curiosity…what’s tilted the scales in making YUI 3 your favorite Javascript library? I know DOM manipulation seems much easier in YUI 3 than 2. Do you like it better than jQuery?
John on June 16th, 2009 at 8:28 am
@John - To be honest, I’ve never really been a fan of jQuery for enterprise JavaScript application development. Even before I worked at Yahoo!, I preferred YUI’s approach to grouping functionality rather than jQuery’s DOM-wrapping and selector-based approach. That being said, I like YUI 3 better than YUI 2.
Nicholas C. Zakas on June 16th, 2009 at 11:13 pm
Any plans for YUI 2.6.*?
We have been using YUI for quite a while. We don’t want to switch to YUI 3 soon.
Regards,
Sandeep
Sandeep on June 20th, 2009 at 2:15 am
Sandeep - I’ve just uploaded a YUI 2 version to my GitHub project. Enjoy!
Nicholas C. Zakas on June 20th, 2009 at 1:45 pm
[...] http://www.nczonline.net/blog/2009/06/02/detecting-if-the-user-is-idle-with-javascript-and-yui-3/ [...]
YUI 3 学习资源 - 岁月如歌 on June 21st, 2009 at 3:39 am
[...] Detecting if the User is Idle with JavaScript and YUI 3: From YUI contributor Nicholas C. Zakas: “Web developers have been interested in whether or not a user is idle since the Ajax explosion hit. With the introduction of more dynamic, highly interactive web interfaces came the desire to know if the user was actually doing anything at any point in time. Thus, the quest for determining if the user is idle began.” Read on for full details on his blog. (Original source.) [...]
In the Wild for June 29, 2009 » Yahoo! User Interface Blog on June 29th, 2009 at 1:07 pm
[...] Irish has ported a YUI3 script to jQuery for his idleTimer plugin. The plugin detects when a user has become [...]
This Week in jQuery, vol. 7 | Jquery Cave on October 12th, 2009 at 2:30 pm
[...] Irish has ported a YUI3 script to jQuery for his idleTimer plugin. The plugin detects when a user has become [...]
This Week in jQuery, vol. 7 « Articles on October 14th, 2009 at 8:37 pm
[...] contributions for the launch, and immediately I thought of the IdleTimer I introduced in my post, Detecting if the user is idle with JavaScript and YUI 3. I had to make a few changes to comply with the gallery’s posting rules, but the [...]
YUI 3 IdleTimer now available on YUI Gallery | NCZOnline on October 28th, 2009 at 3:14 pm
[...] couple of months ago I came across Nick Zakas’ YUI approach to this problem, and recently I found Paul Irish’s jQuery plugin which was based on [...]
MooTools IdleTimer | Rexxars.com on December 14th, 2009 at 6:30 am
[...] C. Zakas, implementó hace ya tiempo una versión para YUI que los desarrolladores que usen pueden tener en cuenta. ← Mozilla Firefox 3.6 está entre [...]
Idle Spy para MooTools, usa Ajax solo cuando sea necesario | aNieto2K on January 23rd, 2010 at 6:37 am
[...] YUI: Detecting if the user is idle with JavaScript and YUI 3 [...]
Idle Spy para Mootools - Detectando la ausencia de interacción - AJAX24 on January 23rd, 2010 at 7:04 am
Leave a Comment