22

How can I track the browser idle time? I am using IE8.

I am not using any session management and don't want to handle it on server side.

Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
BOSS
  • 2,931
  • 8
  • 28
  • 53
  • 1
    How do you want to achieve session managment without handling it on the server side ? – mas-designs Mar 05 '12 at 09:54
  • It's funny how someone asks about client-side timeouts, that everyone suddenly assumes you have server-side session timeouts! – Jeach Jan 25 '23 at 02:27
  • What would be really amazing, is if the browsers got together to add a new HTML/JavaScript API to do just that. So you could give it an interest, such as: window out of focus, clicks, scrolls, keyboard input, xhr, url bar, etc. When any input is received in browser, it resets your timer (ie: 15 min). If/when time hits zero, an event handler is called. See it as similar to a watchdog. That would prevent us managing it at every page in SPA or in client-side router, etc. – Jeach Jan 25 '23 at 02:34

5 Answers5

71

Here is pure JavaScript way to track the idle time and when it reach certain limit do some action:

var IDLE_TIMEOUT = 60; //seconds
var _idleSecondsTimer = null;
var _idleSecondsCounter = 0;

document.onclick = function() {
    _idleSecondsCounter = 0;
};

document.onmousemove = function() {
    _idleSecondsCounter = 0;
};

document.onkeypress = function() {
    _idleSecondsCounter = 0;
};

_idleSecondsTimer = window.setInterval(CheckIdleTime, 1000);

function CheckIdleTime() {
     _idleSecondsCounter++;
     var oPanel = document.getElementById("SecondsUntilExpire");
     if (oPanel)
         oPanel.innerHTML = (IDLE_TIMEOUT - _idleSecondsCounter) + "";
    if (_idleSecondsCounter >= IDLE_TIMEOUT) {
        window.clearInterval(_idleSecondsTimer);
        alert("Time expired!");
        document.location.href = "logout.html";
    }
}
#SecondsUntilExpire { background-color: yellow; }
You will be auto logged out in <span id="SecondsUntilExpire"></span> seconds.

​This code will wait 60 seconds before showing alert and redirecting, and any action will "reset" the count - mouse click, mouse move or key press.

It should be as cross browser as possible, and straight forward. It also support showing the remaining time, if you add element to your page with ID of SecondsUntilExpire.

The above code should work fine, however has several downsides, e.g. it does not allow any other events to run and does not support multiply tabs. Refactored code that include both of these is following: (no need to change HTML)

var IDLE_TIMEOUT = 60; //seconds
var _localStorageKey = 'global_countdown_last_reset_timestamp';
var _idleSecondsTimer = null;
var _lastResetTimeStamp = (new Date()).getTime();
var _localStorage = null;

AttachEvent(document, 'click', ResetTime);
AttachEvent(document, 'mousemove', ResetTime);
AttachEvent(document, 'keypress', ResetTime);
AttachEvent(window, 'load', ResetTime);

try {
    _localStorage = window.localStorage;
}
catch (ex) {
}

_idleSecondsTimer = window.setInterval(CheckIdleTime, 1000);

function GetLastResetTimeStamp() {
    var lastResetTimeStamp = 0;
    if (_localStorage) {
        lastResetTimeStamp = parseInt(_localStorage[_localStorageKey], 10);
        if (isNaN(lastResetTimeStamp) || lastResetTimeStamp < 0)
            lastResetTimeStamp = (new Date()).getTime();
    } else {
        lastResetTimeStamp = _lastResetTimeStamp;
    }

    return lastResetTimeStamp;
}

function SetLastResetTimeStamp(timeStamp) {
    if (_localStorage) {
        _localStorage[_localStorageKey] = timeStamp;
    } else {
        _lastResetTimeStamp = timeStamp;
    }
}

function ResetTime() {
    SetLastResetTimeStamp((new Date()).getTime());
}

function AttachEvent(element, eventName, eventHandler) {
    if (element.addEventListener) {
        element.addEventListener(eventName, eventHandler, false);
        return true;
    } else if (element.attachEvent) {
        element.attachEvent('on' + eventName, eventHandler);
        return true;
    } else {
        //nothing to do, browser too old or non standard anyway
        return false;
    }
}

function WriteProgress(msg) {
    var oPanel = document.getElementById("SecondsUntilExpire");
    if (oPanel)
         oPanel.innerHTML = msg;
    else if (console)
        console.log(msg);
}

function CheckIdleTime() {
    var currentTimeStamp = (new Date()).getTime();
    var lastResetTimeStamp = GetLastResetTimeStamp();
    var secondsDiff = Math.floor((currentTimeStamp - lastResetTimeStamp) / 1000);
    if (secondsDiff <= 0) {
        ResetTime();
        secondsDiff = 0;
    }
    WriteProgress((IDLE_TIMEOUT - secondsDiff) + "");
    if (secondsDiff >= IDLE_TIMEOUT) {
        window.clearInterval(_idleSecondsTimer);
        ResetTime();
        alert("Time expired!");
        document.location.href = "logout.html";
    }
}

The refactored code above is using local storage to keep track of when the counter was last reset, and also reset it on each new tab that is opened which contains the code, then the counter will be the same for all tabs, and resetting in one will result in reset of all tabs. Since Stack Snippets do not allow local storage, it's pointless to host it there so I've made a fiddle: http://jsfiddle.net/yahavbr/gpvqa0fj/3/

Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
  • Why not just set a timeout to 60 seconds and have those mouse/keyboard events clear the timeout? – JacobEvelyn Aug 09 '12 at 12:39
  • 2
    I mean instead of calling `CheckIdleTime()` every second, you could just set the timeout to 60 seconds. I guess you can't show the remaining time, but it is a lot simpler. Your solution is also a little different from what the OP seems to be asking. – JacobEvelyn Sep 05 '12 at 18:31
  • @ConstableJoe I see. Well, valid way as well but can't see anything wrong in my way. Browser can handle such a timer just fine. As for OP he accepted the answer without any comment so I can only conclude it was what he was looking for. – Shadow The GPT Wizard Sep 05 '12 at 18:45
  • How to get confirmation from user before redirect? – CJ Ramki Nov 15 '13 at 12:17
  • 1
    @CJRamki just have `if (confirm("Sure you want to leave?") { document.location.href = "logout.html"; }`. – Shadow The GPT Wizard Nov 15 '13 at 13:08
  • @ShadowWizard I completely agree with Jacob, Though your solution is valid and works great, but still checking by using setInterval is a bit more load consuming to browser, as it checks every second, rather than that, setTimeout is a bit less load consuming. – Sanjay Mar 03 '15 at 06:53
  • 1
    @Yegya but then I won't be able to show the amount of time left, like I'm doing in the sample code. While not really important, it's a nice to have feature that is not possible without `setInterval()` – Shadow The GPT Wizard Mar 03 '15 at 07:33
  • @ShadowWizard Yes completely agree wid u bro, not offending your code, if ignoring the time left situation, which is practical in most of the situation like in game inactivity time, tracking / monitoring ux browser activity etc, setTimeout is the best choice. But yes, when it actually matters for time left display, the above code is the ideal solution. Thumbs up for that. and actually I've up vote your solution as well. – Sanjay Mar 04 '15 at 04:58
  • 1
    @ShadowWizard Someone has [copied](http://stackoverflow.com/a/13246534/2246380) your code and has got more reputation off it than you. Just letting you know. – Ram Apr 21 '15 at 18:46
  • Should one use `document.addEventListener('mousemove', callback, true)` etc. because it listens for event capture not bubbling? I think the provided code would have issues if user code calls stopPropagation on the events utilized in the code snippet. https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener – thesmart Oct 27 '15 at 00:23
  • @Shadow Wizard your code is good and it solved my problem. I put it in my footer so that it will be used in all pages. Now new problem arises when i open two tabs at same time. I am working in one tab, but second tab is idle and got logged out. can we have some global variable common is all tabs? – number50 Nov 25 '15 at 13:29
  • @number50 good point! Guess using local storage will work just fine, these days it's supported by all major browsers. Wanted to refactor the code anyway for a while, so will add this as well soon. – Shadow The GPT Wizard Nov 25 '15 at 13:35
  • @Shadow Wizard Thank you very much. You are so quick and your code is so nice. I was trying since last two weeks and you take me to the solution. Thanks again. – number50 Nov 25 '15 at 17:10
  • @number50 cheers, I admit this multi tab thing wasn't trivial to implement. :) – Shadow The GPT Wizard Nov 25 '15 at 17:14
  • Thanks Shadow Wizard! Great code! Very useful!I had a bit of trouble with first time login of the site. Since localstorage remembers the previous value, it seems that on ready, it detects that the system has already timed out and directs me to logout page immediately. I solved it by adding a simple cookie where the cookie tracks firstvisit. Logout code will only run if it is not the firstvisit. Once browser closes, the cookie is simply deleted and created again next visit. – Water Jan 17 '16 at 12:33
  • @Yegya Is the interval really more consuming than clearing and setting new timeout every time? – Qwerty Feb 16 '16 at 11:11
  • onmousemove is bad in chrome: http://stackoverflow.com/questions/14363708/jquery-mousemove-workaround – wutzebaer Jun 08 '16 at 13:07
  • @wutzebaer no. It's not working properly for bunch of people, most likely due to some funky stuff they have on their machine. It's still working just fine for all the millions of other people. Not a reason to dump it, or look for ultra complicated workarounds. – Shadow The GPT Wizard Jun 08 '16 at 13:39
  • it's not working when the page automatically updates some content on the page, but the workaround is simple: `if (event.type === 'mousemove' && event.originalEvent.movementX === 0 && event.originalEvent.movementX === 0) return;` – wutzebaer Jun 08 '16 at 13:58
  • @wutzebaer originalEvent belongs to jQuery, it doesn't exist in native JS. I would really rather not turn this into jQuery to fix bad behavior of browsers or plugins that misuse the event object to trigger fake mouse movement. Feel free to post a new answer, with jQuery solution for the original question - even though it's not tagged jQuery, these days it's common enough. – Shadow The GPT Wizard Jun 08 '16 at 14:03
  • This works so nice in my all 90% pages of my website. I face issue when page has any iframe. Is there any way to track any mouse movement including inside iframe? – Chetan Patel Sep 07 '17 at 12:33
  • 1
    @ChetanPatel only if the iframe contains page in the same domain. – Shadow The GPT Wizard Sep 07 '17 at 12:53
3

Too late to reply, but this might help someone to write clean and practical solution. This is an ideal solution, when you do not need to display time left for session expire. Good to ignore setInterval(), which keeps on running the script through out the application running time.

    var inactivityTimeOut = 10 * 1000, //10 seconds
        inactivitySessionExpireTimeOut = '';

    setSessionExpireTimeOut();

    function setSessionExpireTimeOut () {
        'use strict';
        
        clearSessionExpireTimeout();
        inactivitySessionExpireTimeOut = setTimeout(function() {
            expireSessionIdleTime();
        }, inactivityTimeOut);
    }

    function expireSessionIdleTime () {
        'use strict';

        console.log('user inactive for ' + inactivityTimeOut + " seconds");
        console.log('session expired');
        alert('time expired');
        clearSessionExpireTimeout();
        document.location.href = "logout.html";
    }

    function clearSessionExpireTimeout () {
            'use strict';

            clearTimeout(inactivitySessionExpireTimeOut);
    }
Running example: Timeout alert will be popped up in 10 seconds
Sanjay
  • 1,595
  • 1
  • 17
  • 29
3

Hope this is what you are looking for jquery-idletimer-plugin

me_digvijay
  • 5,374
  • 9
  • 46
  • 83
  • 1
    Actually its good to ignore plugin, when you can achieve the same with very simple script. – Sanjay Mar 04 '15 at 05:00
  • 1
    @Yegya in general, that's true, but this plugin was written by Paul Irish, who is well-known in the industry, so I would be more confident in his code. – daveslab Jun 11 '16 at 15:12
1

Here's an approach using jquery as I needed to preserve existing keyboard events on the document.

I also needed to do different things at different idle times so I wrapped it in a function

var onIdle = function (timeOutSeconds,func){
    //Idle detection
    var idleTimeout;
    var activity=function() {
        clearTimeout(idleTimeout);
        console.log('to cleared');
        idleTimeout = setTimeout(func, timeOutSeconds * 1000);
    }
    $(document).on('mousedown mousemove keypress',activity);
    activity();
}
onIdle(60*60,function(){
    location.reload();
});
onIdle(30,function(){
    if(currentView!=='welcome'){
        loadView('welcome');
    }
});
Keegan 82
  • 394
  • 2
  • 11
1

I needed a similar thing and created this :https://github.com/harunurhan/idlejs

It simple, configurable and powerful in a way, without any dependencies. Here's an example.

import { Idle } from 'idlejs/dist';

// with predefined events on `document`
const idle = new Idle()
  .whenNotInteractive()
  .within(60)
  .do(() => console.log('IDLE'))
  .start();

You can also use custom event targets and events

const idle = new Idle()
  .whenNot([{
    events: ['click', 'hover'],
    target: buttonEl,
  },
  {
    events: ['click', 'input'],
    target: inputEl,
  },
  ])
  .within(10)
  .do(() => called = true)
  .start();

(Written in typescript and compiled to es5)

harunurhan
  • 1,516
  • 1
  • 17
  • 21