Related
Is it possible to detect "idle" time in JavaScript?
My primary use case probably would be to pre-fetch or preload content.
I define idle time as a period of user inactivity or without any CPU usage
Here is a simple script using jQuery that handles mousemove and keypress events.
If the time expires, the page reloads.
<script type="text/javascript">
var idleTime = 0;
$(document).ready(function () {
// Increment the idle time counter every minute.
var idleInterval = setInterval(timerIncrement, 60000); // 1 minute
// Zero the idle timer on mouse movement.
$(this).mousemove(function (e) {
idleTime = 0;
});
$(this).keypress(function (e) {
idleTime = 0;
});
});
function timerIncrement() {
idleTime = idleTime + 1;
if (idleTime > 19) { // 20 minutes
window.location.reload();
}
}
</script>
With vanilla JavaScript:
var inactivityTime = function () {
var time;
window.onload = resetTimer;
// DOM Events
document.onmousemove = resetTimer;
document.onkeydown = resetTimer;
function logout() {
alert("You are now logged out.")
//location.href = 'logout.html'
}
function resetTimer() {
clearTimeout(time);
time = setTimeout(logout, 3000)
// 1000 milliseconds = 1 second
}
};
And initialise the function where you need it (for example: onPageLoad).
window.onload = function() {
inactivityTime();
}
You can add more DOM events if you need to. Most used are:
document.onload = resetTimer;
document.onmousemove = resetTimer;
document.onmousedown = resetTimer; // touchscreen presses
document.ontouchstart = resetTimer;
document.onclick = resetTimer; // touchpad clicks
document.onkeydown = resetTimer; // onkeypress is deprectaed
document.addEventListener('scroll', resetTimer, true); // improved; see comments
Or register desired events using an array
window.addEventListener('load', resetTimer, true);
var events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart'];
events.forEach(function(name) {
document.addEventListener(name, resetTimer, true);
});
DOM Events list: http://www.w3schools.com/jsref/dom_obj_event.asp
Remember to use window, or document according your needs. Here you can see the differences between them: What is the difference between window, screen, and document in JavaScript?
Code Updated with #frank-conijn and #daxchen improve: window.onscroll will not fire if scrolling is inside a scrollable element, because scroll events don't bubble. In window.addEventListener('scroll', resetTimer, true), the third argument tells the listener to catch the event during the capture phase instead of the bubble phase.
Improving on Equiman's (original) answer:
function idleLogout() {
var t;
window.onload = resetTimer;
window.onmousemove = resetTimer;
window.onmousedown = resetTimer; // catches touchscreen presses as well
window.ontouchstart = resetTimer; // catches touchscreen swipes as well
window.ontouchmove = resetTimer; // required by some devices
window.onclick = resetTimer; // catches touchpad clicks as well
window.onkeydown = resetTimer;
window.addEventListener('scroll', resetTimer, true); // improved; see comments
function yourFunction() {
// your function for too long inactivity goes here
// e.g. window.location.href = 'logout.php';
}
function resetTimer() {
clearTimeout(t);
t = setTimeout(yourFunction, 10000); // time is in milliseconds
}
}
idleLogout();
Apart from the improvements regarding activity detection, and the change from document to window, this script actually calls the function, rather than letting it sit idle by.
It doesn't catch zero CPU usage directly, but that is impossible, because executing a function causes CPU usage. And user inactivity eventually leads to zero CPU usage, so indirectly it does catch zero CPU usage.
I have created a small library that does this:
https://github.com/shawnmclean/Idle.js
Description:
Tiny JavaScript library to report activity of user in the browser
(away, idle, not looking at webpage, in a different tab, etc). that is independent of any
other JavaScript libraries such as jQuery.
Visual Studio users can get it from NuGet by:
Install-Package Idle.js
Here is a rough jQuery implementation of tvanfosson's idea:
$(document).ready(function(){
idleTime = 0;
//Increment the idle time counter every second.
var idleInterval = setInterval(timerIncrement, 1000);
function timerIncrement()
{
idleTime++;
if (idleTime > 2)
{
doPreload();
}
}
//Zero the idle timer on mouse movement.
$(this).mousemove(function(e){
idleTime = 0;
});
function doPreload()
{
//Preload images, etc.
}
})
Similar to Peter J's solution (with a jQuery custom event)...
// Use the jquery-idle-detect.js script below
$(window).on('idle:start', function() {
// Start your prefetch, etc. here...
});
$(window).on('idle:stop', function() {
// Stop your prefetch, etc. here...
});
File jquery-idle-detect.js
(function($, $w) {
// Expose configuration option
// Idle is triggered when no events for 2 seconds
$.idleTimeout = 2000;
// Currently in idle state
var idle = false;
// Handle to idle timer for detection
var idleTimer = null;
// Start the idle timer and bind events on load (not DOM-ready)
$w.on('load', function() {
startIdleTimer();
$w.on('focus resize mousemove keyup', startIdleTimer)
.on('blur', idleStart) // Force idle when in a different tab/window
;
]);
function startIdleTimer() {
clearTimeout(idleTimer); // Clear prior timer
if (idle) $w.trigger('idle:stop'); // If idle, send stop event
idle = false; // Not idle
var timeout = ~~$.idleTimeout; // Option to integer
if (timeout <= 100)
timeout = 100; // Minimum 100 ms
if (timeout > 300000)
timeout = 300000; // Maximum 5 minutes
idleTimer = setTimeout(idleStart, timeout); // New timer
}
function idleStart() {
if (!idle)
$w.trigger('idle:start');
idle = true;
}
}(window.jQuery, window.jQuery(window)))
You can do it more elegantly with Underscore.js and jQuery:
$('body').on("click mousemove keyup", _.debounce(function(){
// do preload here
}, 1200000)) // 20 minutes debounce
My answer was inspired by vijay's answer, but is a shorter, more general solution that I thought I'd share for anyone it might help.
(function () {
var minutes = true; // change to false if you'd rather use seconds
var interval = minutes ? 60000 : 1000;
var IDLE_TIMEOUT = 3; // 3 minutes in this example
var idleCounter = 0;
document.onmousemove = document.onkeypress = function () {
idleCounter = 0;
};
window.setInterval(function () {
if (++idleCounter >= IDLE_TIMEOUT) {
window.location.reload(); // or whatever you want to do
}
}, interval);
}());
As it currently stands, this code will execute immediately and reload your current page after 3 minutes of no mouse movement or key presses.
This utilizes plain vanilla JavaScript and an immediately-invoked function expression to handle idle timeouts in a clean and self-contained manner.
All the previous answers have an always-active mousemove handler. If the handler is jQuery, the additional processing jQuery performs can add up. Especially if the user is using a gaming mouse, as many as 500 events per second can occur.
This solution avoids handling every mousemove event. This result in a small timing error, but which you can adjust to your need.
function setIdleTimeout(millis, onIdle, onUnidle) {
var timeout = 0;
startTimer();
function startTimer() {
timeout = setTimeout(onExpires, millis);
document.addEventListener("mousemove", onActivity);
document.addEventListener("keydown", onActivity);
document.addEventListener("touchstart", onActivity);
}
function onExpires() {
timeout = 0;
onIdle();
}
function onActivity() {
if (timeout) clearTimeout(timeout);
else onUnidle();
//since the mouse is moving, we turn off our event hooks for 1 second
document.removeEventListener("mousemove", onActivity);
document.removeEventListener("keydown", onActivity);
document.removeEventListener("touchstart", onActivity);
setTimeout(startTimer, 1000);
}
}
http://jsfiddle.net/9exz43v2/
I had the same issue and I found a quite good solution.
I used jquery.idle and I only needed to do:
$(document).idle({
onIdle: function(){
alert('You did nothing for 5 seconds');
},
idle: 5000
})
See JsFiddle demo.
(Just for information: see this for back-end event tracking Leads browserload)
If you are targeting a supported browser (Chrome or Firefox as of December 2018) you can experiment with the requestIdleCallback and include the requestIdleCallback shim for unsupported browsers.
You could probably hack something together by detecting mouse movement on the body of the form and updating a global variable with the last movement time. You'd then need to have an interval timer running that periodically checks the last movement time and does something if it has been sufficiently long since the last mouse movement was detected.
I wrote a small ES6 class to detect activity and otherwise fire events on idle timeout. It covers keyboard, mouse and touch, can be activated and deactivated and has a very lean API:
const timer = new IdleTimer(() => alert('idle for 1 minute'), 1000 * 60 * 1);
timer.activate();
It does not depend on jQuery, though you might need to run it through Babel to support older browsers.
https://gist.github.com/4547ef5718fd2d31e5cdcafef0208096
(Partially inspired by the good core logic of Equiman's answer.)
sessionExpiration.js
sessionExpiration.js is lightweight yet effective and customizable. Once implemented, use in just one row:
sessionExpiration(idleMinutes, warningMinutes, logoutUrl);
Affects all tabs of the browser, not just one.
Written in pure JavaScript, with no dependencies. Fully client side.
(If so wanted.) Has warning banner and countdown clock, that is cancelled by user interaction.
Simply include the sessionExpiration.js, and call the function, with arguments [1] number of idle minutes (across all tabs) until user is logged out, [2] number of idle minutes until warning and countdown is displayed, and [3] logout url.
Put the CSS in your stylesheet. Customize it if you like. (Or skip and delete banner if you don't want it.)
If you do want the warning banner however, then you must put an empty div with ID sessExpirDiv on your page (a suggestion is putting it in the footer).
Now the user will be logged out automatically if all tabs have been inactive for the given duration.
Optional: You may provide a fourth argument (URL serverRefresh) to the function, so that a server side session timer is also refreshed when you interact with the page.
This is an example of what it looks like in action, if you don't change the CSS.
Try this code. It works perfectly.
var IDLE_TIMEOUT = 10; //seconds
var _idleSecondsCounter = 0;
document.onclick = function () {
_idleSecondsCounter = 0;
};
document.onmousemove = function () {
_idleSecondsCounter = 0;
};
document.onkeypress = function () {
_idleSecondsCounter = 0;
};
window.setInterval(CheckIdleTime, 1000);
function CheckIdleTime() {
_idleSecondsCounter++;
var oPanel = document.getElementById("SecondsUntilExpire");
if (oPanel)
oPanel.innerHTML = (IDLE_TIMEOUT - _idleSecondsCounter) + "";
if (_idleSecondsCounter >= IDLE_TIMEOUT) {
alert("Time expired!");
document.location.href = "SessionExpired.aspx";
}
}
<script type="text/javascript">
var idleTime = 0;
$(document).ready(function () {
//Increment the idle time counter every minute.
idleInterval = setInterval(timerIncrement, 60000); // 1 minute
//Zero the idle timer on mouse movement.
$('body').mousemove(function (e) {
//alert("mouse moved" + idleTime);
idleTime = 0;
});
$('body').keypress(function (e) {
//alert("keypressed" + idleTime);
idleTime = 0;
});
$('body').click(function() {
//alert("mouse moved" + idleTime);
idleTime = 0;
});
});
function timerIncrement() {
idleTime = idleTime + 1;
if (idleTime > 10) { // 10 minutes
window.location.assign("http://www.google.com");
}
}
</script>
I think this jQuery code is perfect one, though copied and modified from above answers!!
Do not forgot to include the jQuery library in your file!
Pure JavaScript with a properly set reset time and bindings via addEventListener:
(function() {
var t,
timeout = 5000;
function resetTimer() {
console.log("reset: " + new Date().toLocaleString());
if (t) {
window.clearTimeout(t);
}
t = window.setTimeout(logout, timeout);
}
function logout() {
console.log("done: " + new Date().toLocaleString());
}
resetTimer();
//And bind the events to call `resetTimer()`
["click", "mousemove", "keypress"].forEach(function(name) {
console.log(name);
document.addEventListener(name, resetTimer);
});
}());
The problem with all these solutions, although correct, is they are impractical, when taking into account the session timeout valuable set, using PHP, .NET or in the Application.cfc file for ColdFusion developers.
The time set by the above solution needs to sync with the server-side session timeout. If the two do not sync, you can run into problems that will just frustrate and confuse your users.
For example, the server side session timeout might be set to 60 minutes, but the user may believe that he/she is safe, because the JavaScript idle time capture has increased the total amount of time a user can spend on a single page. The user may have spent time filling in a long form, and then goes to submit it. The session timeout might kick in before the form submission is processed.
I tend to just give my users 180 minutes, and then use JavaScript to automatically log the user out. Essentially, using some of the code above, to create a simple timer, but without the capturing mouse event part.
In this way my client side and server-side time syncs perfectly. There is no confusion, if you show the time to the user in your UI, as it reduces. Each time a new page is accessed in the CMS, the server side session and JavaScript timer are reset. Simple and elegant. If a user stays on a single page for more than 180 minutes, I figure there is something wrong with the page, in the first place.
You can use the below mentioned solution
var idleTime;
$(document).ready(function () {
reloadPage();
$('html').bind('mousemove click mouseup mousedown keydown keypress keyup submit change mouseenter scroll resize dblclick', function () {
clearTimeout(idleTime);
reloadPage();
});
});
function reloadPage() {
clearTimeout(idleTime);
idleTime = setTimeout(function () {
location.reload();
}, 3000);
}
I wrote a simple jQuery plugin that will do what you are looking for.
https://github.com/afklondon/jquery.inactivity
$(document).inactivity( {
interval: 1000, // the timeout until the inactivity event fire [default: 3000]
mouse: true, // listen for mouse inactivity [default: true]
keyboard: false, // listen for keyboard inactivity [default: true]
touch: false, // listen for touch inactivity [default: true]
customEvents: "customEventName", // listen for custom events [default: ""]
triggerAll: true, // if set to false only the first "activity" event will be fired [default: false]
});
The script will listen for mouse, keyboard, touch and other custom events inactivity (idle) and fire global "activity" and "inactivity" events.
I have tested this code working file:
var timeout = null;
var timee = '4000'; // default time for session time out.
$(document).bind('click keyup mousemove', function(event) {
if (timeout !== null) {
clearTimeout(timeout);
}
timeout = setTimeout(function() {
timeout = null;
console.log('Document Idle since '+timee+' ms');
alert("idle window");
}, timee);
});
Is it possible to have a function run every 10 seconds, and have that check a "counter" variable? If that's possible, you can have an on mouseover for the page, can you not?
If so, use the mouseover event to reset the "counter" variable. If your function is called, and the counter is above the range that you pre-determine, then do your action.
Here is the best solution I have found:
Fire Event When User is Idle
Here is the JavaScript:
idleTimer = null;
idleState = false;
idleWait = 2000;
(function ($) {
$(document).ready(function () {
$('*').bind('mousemove keydown scroll', function () {
clearTimeout(idleTimer);
if (idleState == true) {
// Reactivated event
$("body").append("<p>Welcome Back.</p>");
}
idleState = false;
idleTimer = setTimeout(function () {
// Idle Event
$("body").append("<p>You've been idle for " + idleWait/1000 + " seconds.</p>");
idleState = true; }, idleWait);
});
$("body").trigger("mousemove");
});
}) (jQuery)
I use this approach, since you don't need to constantly reset the time when an event fires. Instead, we just record the time, and this generates the idle start point.
function idle(WAIT_FOR_MINS, cb_isIdle) {
var self = this,
idle,
ms = (WAIT_FOR_MINS || 1) * 60000,
lastDigest = new Date(),
watch;
//document.onmousemove = digest;
document.onkeypress = digest;
document.onclick = digest;
function digest() {
lastDigest = new Date();
}
// 1000 milisec = 1 sec
watch = setInterval(function() {
if (new Date() - lastDigest > ms && cb_isIdel) {
clearInterval(watch);
cb_isIdle();
}
}, 1000*60);
},
Based on the inputs provided by equiman:
class _Scheduler {
timeoutIDs;
constructor() {
this.timeoutIDs = new Map();
}
addCallback = (callback, timeLapseMS, autoRemove) => {
if (!this.timeoutIDs.has(timeLapseMS + callback)) {
let timeoutID = setTimeout(callback, timeLapseMS);
this.timeoutIDs.set(timeLapseMS + callback, timeoutID);
}
if (autoRemove !== false) {
setTimeout(
this.removeIdleTimeCallback, // Remove
10000 + timeLapseMS, // 10 secs after
callback, // the callback
timeLapseMS, // is invoked.
);
}
};
removeCallback = (callback, timeLapseMS) => {
let timeoutID = this.timeoutIDs.get(timeLapseMS + callback);
if (timeoutID) {
clearTimeout(timeoutID);
this.timeoutIDs.delete(timeLapseMS + callback);
}
};
}
class _IdleTimeScheduler extends _Scheduler {
events = [
'load',
'mousedown',
'mousemove',
'keydown',
'keyup',
'input',
'scroll',
'touchstart',
'touchend',
'touchcancel',
'touchmove',
];
callbacks;
constructor() {
super();
this.events.forEach(name => {
document.addEventListener(name, this.resetTimer, true);
});
this.callbacks = new Map();
}
addIdleTimeCallback = (callback, timeLapseMS) => {
this.addCallback(callback, timeLapseMS, false);
let callbacksArr = this.callbacks.get(timeLapseMS);
if (!callbacksArr) {
this.callbacks.set(timeLapseMS, [callback]);
} else {
if (!callbacksArr.includes(callback)) {
callbacksArr.push(callback);
}
}
};
removeIdleTimeCallback = (callback, timeLapseMS) => {
this.removeCallback(callback, timeLapseMS);
let callbacksArr = this.callbacks.get(timeLapseMS);
if (callbacksArr) {
let index = callbacksArr.indexOf(callback);
if (index !== -1) {
callbacksArr.splice(index, 1);
}
}
};
resetTimer = () => {
for (let [timeLapseMS, callbacksArr] of this.callbacks) {
callbacksArr.forEach(callback => {
// Clear the previous IDs
let timeoutID = this.timeoutIDs.get(timeLapseMS + callback);
clearTimeout(timeoutID);
// Create new timeout IDs.
timeoutID = setTimeout(callback, timeLapseMS);
this.timeoutIDs.set(timeLapseMS + callback, timeoutID);
});
}
};
}
export const Scheduler = new _Scheduler();
export const IdleTimeScheduler = new _IdleTimeScheduler();
As simple as it can get, detect when the mouse moves only:
var idle = false;
document.querySelector('body').addEventListener('mousemove', function(e) {
if(idle!=false)
idle = false;
});
var idleI = setInterval(function()
{
if(idle == 'inactive')
{
return;
}
if(idle == true)
{
idleFunction();
idle = 'inactive';
return;
}
idle = true;
}, 30000); // half the expected time. Idle will trigger after 60 s in this case.
function idleFuntion()
{
console.log('user is idle');
}
Here is an AngularJS service for accomplishing in Angular.
/* Tracks now long a user has been idle. secondsIdle can be polled
at any time to know how long user has been idle. */
fuelServices.factory('idleChecker',['$interval', function($interval){
var self = {
secondsIdle: 0,
init: function(){
$(document).mousemove(function (e) {
self.secondsIdle = 0;
});
$(document).keypress(function (e) {
self.secondsIdle = 0;
});
$interval(function(){
self.secondsIdle += 1;
}, 1000)
}
}
return self;
}]);
Keep in mind this idle checker will run for all routes, so it should be initialized in .run() on load of the angular app. Then you can use idleChecker.secondsIdle inside each route.
myApp.run(['idleChecker',function(idleChecker){
idleChecker.init();
}]);
Surely you want to know about window.requestIdleCallback(), which queues a function to be called during a browser's idle periods.
You can see an elegant usage of this API in the Quicklink repo.
const requestIdleCallback = window.requestIdleCallback ||
function (cb) {
const start = Date.now();
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
},
});
}, 1);
};
The meaning of the code above is: if the browser supports requestIdleCallback (check the compatibility), uses it. If is not supported, uses a setTimeout(()=> {}, 1) as fallback, which should queue the function to be called at the end of the event loop.
Then you can use it like this:
requestIdleCallback(() => {...}, {
timeout: 2000
});
The second parameter is optional, you might want to set a timeout if you want to make sure the function is executed.
You could probably detect inactivity on your web page using the mousemove tricks listed, but that won't tell you that the user isn't on another page in another window or tab, or that the user is in Word or Photoshop, or WoW and just isn't looking at your page at this time.
Generally, I'd just do the prefetch and rely on the client's multi-tasking. If you really need this functionality, you do something with an ActiveX control in Windows, but it's ugly at best.
Debounce is actually a great idea! Here is a version for jQuery-free projects:
const derivedLogout = createDerivedLogout(30);
derivedLogout(); // It could happen that the user is too idle)
window.addEventListener('click', derivedLogout, false);
window.addEventListener('mousemove', derivedLogout, false);
window.addEventListener('keyup', derivedLogout, false);
function createDerivedLogout (sessionTimeoutInMinutes) {
return _.debounce( () => {
window.location = this.logoutUrl;
}, sessionTimeoutInMinutes * 60 * 1000 )
}
I'm unable to figure out where a syntax logical error resides in the script furthest below. Essentially what it does is it alerts people that they must wait 1.5 seconds before they can answer a radio-button type question and automatically move on to the next page. No alert if they spend more than 1.5 seconds.
This script was written for only one click event, but I need it to work for two nested events, where clicking on a radio button option automatically triggers the "next" button to move on to the next page. For example if you take out the following event (and its end brackets), it works well:
$("[class*=bfasg] .radio").click(function(){
I've checked the syntax at Esprima to make sure the brackets are correct, so the problem lies elsewhere.
$(document).ready(function() {
minTime(1.5);
function minTime(minTime) {
var startTime = new Date();
$("[class*=bfasg] .radio").click(function(){$("#movenextbtn").click(function(){
var endTime = new Date();
if((endTime - startTime)/1000 <= minTime) {
alert('You must spend at least '+minTime+' seconds on the question.');
return false;
}
else {
return true;
}
});
});
}
});
Any experts out there who can detect the problem?
(See answer to updated question below)
It's not a syntax error. It's a logic error.
It becomes slightly clearer if you format the code consistently:
$(document).ready(function () {
minTime(1.5);
function minTime(minTime) {
var startTime = new Date();
// Hooking up a click handler
$("[class*=bfasg] .radio").click(function () {
// This code doesn't run until/unless someone clicks
// one of the `[class*=bfasg] .radio` elements.
$("#movenextbtn").click(function () {
var endTime = new Date();
if ((endTime - startTime) / 1000 <= minTime) {
alert('You must spend at least ' + minTime + ' seconds on the question.');
return false;
} else {
return true;
}
});
});
}
});
What you've done there is said "When someone clicks a [class*=bfasg] .radio element, hook up an event handler on the #movenextbtn element."
You probably don't want to wait to hook up the event until someone clicks on a radio button. If your goal is to hook up the click event on both sets of elements, combine them in the same selector as you would in CSS:
$(document).ready(function () {
minTime(1.5);
function minTime(minTime) {
var startTime = new Date();
$("[class*=bfasg] .radio, #movenextbtn").click(function () {
var endTime = new Date();
if ((endTime - startTime) / 1000 <= minTime) {
alert('You must spend at least ' + minTime + ' seconds on the question.');
return false;
}
});
}
});
(By the way, returning true from a jQuery event handler has no meaning, so I've removed it above.)
Below you've commented:
What happens is that I want clicking the radio button to automatically trigger the "Next" button for going to the next page since I have one question per page.
That doesn't fundamentally change things. You haven't shown what the button does to move to the next page, but you'd just put that code in the one click handler above. E.g., you still hook click on both the radio buttons and the button, and you still handle that event using common code. E.g.:
$(document).ready(function () {
minTime(1.5);
function minTime(minTime) {
var startTime = new Date();
$("[class*=bfasg] .radio, #movenextbtn").click(function () {
var endTime = new Date();
if ((endTime - startTime) / 1000 <= minTime) {
alert('You must spend at least ' + minTime + ' seconds on the question.');
return false;
} else {
// ****Move to next page here****
}
});
}
});
Alternately, you could have the radio button click trigger a click event on the button, like this:
$(document).ready(function () {
minTime(1.5);
function minTime(minTime) {
var startTime = new Date();
// Hook up `click` on the radio buttons
$("[class*=bfasg] .radio").click(function () {
// Respond to click by firing click on the #movenextbtn
$("#movenextbtn").click();
});
// Hook up `click` on the #movenextbtn
$("#movenextbtn").click(function () {
var endTime = new Date();
if ((endTime - startTime) / 1000 <= minTime) {
alert('You must spend at least ' + minTime + ' seconds on the question.');
return false;
}
});
}
});
I'm not a huge fan of firing synthetic events like that when you could use common logic, but it's an option.
function callMe() {
// Do Something
}
$(document).ready(function() {
callMe();
});
DECLARE FUNCTION outside of ready(), but then define FUNCTION inside of ready().it's better to define them outside of document ready. And, if you need to, place the implementation of the method within the document ready.
I have this button which is not working correctly for hold button for a period (but it works like click only).
Where i was trying to do if the button is hold for greater/equal then 2 seconds then callfunction1, if the button was pressed less then 2 seconds then callfuntion2.
var clickDisabled = false;
function clickLocker() {
/* #Button: 2 seconds */
clickDisabled = true;
setTimeout(function(){clickDisabled = false;}, 2000);
}
function callfunction1() { // you have hold he button for greater then or equal 2 second }
function callfunction2() { // you have hold the button less then 2 second }
$('.button').live("click",function()
{
if (clickDisabled) {
alert("locked for 2 second");
return;
}
clickLocker();
});
I think this solution should work. I have not tested it but it should give you the right idea.
var startTime;
function callfunction1() { // you have hold he button for greater then or equal 2 second }
function callfunction2() { // you have hold the button less then 2 second }
function buttonDownEvent() {
var Time = new Date();
startTime = Time.getTime();
}
function buttonUpEvent() {
if(new Date().getTime() - startTime < 2000)
callfunction2()
else
callfunction1()
}
$('.button').live("mousedown",function()
{
buttonDownEvent();
});
$('.button').live("mouseup",function()
{
buttonUpEvent();
});
Listen for both events, mousedown and mouseup, measuring the time between both:
var timeDown;
var timeUp;
$('.button').live("mousedown",function(){
timeDown = event.timeStamp;
});
$('.button').live("mouseup",function(){
timeUp = event.timeStamp;
time = timeUp-timeDown;
if (time>2000){
function1();
}else{
function2();
}
});
please note that event.timeStamp wont work well in firefox. For firefox you can do (new Date).getTime();
You can do this using events to the mouseup and mousedown events and timing the difference between them. Also, you need to remember which element caused the click - if the user released the mouse on a different element then it should just do the "non 2-second hold" function. A JSFiddle showing this working is available here: http://jsfiddle.net/35rw3/6/.
That was a great suggestion from slash. This is how you can do this
var clickstart;
var clickstop;
$("a").on('mousedown', function(e) {
clickstart = e.timeStamp;
}).on('mouseup', function(e) {
clickstop = e.timeStamp- clickstart
if(clickstop >= 2000) two()
else one();
});
Demo
Updates:
It might be necessary to track the mouse movement like #MarkRhodes wrote in his comments. So for that, check this update
is there any way to detect how many seconds a mouse pointer stays on an html element?
I would like to retrieve how many seconds a mouse stays over element to put a little delay on a callback event... if is possible :)
i'm trying with a simple for() cycle detecting by a counter :
var time_over ;
$('.bean-active').live('mouseover',function(){
id_tag = $(this).attr("id");
for(time_over = 1;time_over <= 3000;time_over ++){
if(time_over == 3000){
$('.bean-bubble,.bean-bubble img').hide();
$('#bean-bubble-'+id_tag+',#bean-bubble-'+id_tag+' img').show();
}
}
});
the problem is that it doesn't works :(
also i would like to bind a mouseleave event, script logic should be:
while ( mouseover element count how many time it stays over)
if (time == n)
{ do somenthing }
if (mouseleave from element earlier then time)
{ do somenthing different }
Given this markup:
<div id="hoverOverMe">Hover over me</div>
<div id="output"></div>
Something like this plugin should do the trick:
(function($) {
$.fn.delayedAction = function(options)
{
var settings = $.extend(
{},
{
delayedAction : function(){},
cancelledAction: function(){},
hoverTime: 1000
},
options);
return this.each(function(){
var $this = $(this);
$this.hover(function(){
$this.data('timerId',
setTimeout(function(){
$this.data('hover',false);
settings.delayedAction($this);
},settings.hoverTime));
$this.data('hover',true);
},
function(){
if($this.data('hover')){
clearTimeout($this.data('timerId'));
settings.cancelledAction($this);
}
$this.data('hover',false);
} );
});
}
})(jQuery);
and the calling code:
$('#hoverOverMe').delayedAction (
{
delayedAction: function($element){
$('#output').html($element.attr('id') + ' was hovered for 3 seconds');
},
cancelledAction: function($element){
$('#output').html($element.attr('id') + ' was hovered for less than 3 seconds');
},
hoverTime: 3000 // 3 seconds
}
);
Live example: http://jsfiddle.net/nrUqS/
For your requirement, something like this should suffice:
$('.bean-active').delayedAction(
{
delayedAction: function($element){
id_tag = $element.attr("id");
$('.bean-bubble,.bean-bubble img').hide();
$('#bean-bubble-'+id_tag+',#bean-bubble-'+id_tag+' img').show();
},
hoverTime: 3000
});
This code will calculate the time in milliseconds that you hover over an element with your mouse:
$(document).ready(function() {
$('#element').bind('mouseenter mouseleave', function(evt) {
var currentTime == new Date();
if (evt.type === 'mouseenter') {
$(this).data('mouseenterTime') == currentTime.getTime();
} else if (evt.type === 'mouseleave') {
var mouseoverTime = currentTime.getTime() - $(this).data('mouseenterTime');
alert('mouseover time was: ' + mouseoverTime);
}
})
});
You should be able to utilize the hover() function to capture when the mouse goes over a particular element and then react as desired when the mouse is removed from that object.
$("#someDiv").hover(function(){
//start counter
}, function(){
//stop counter
});
I've used C. Spencer Beggs answer as a template, because his one didn't work for me. I've used simple variables, included lots of console.log messages and corrected '==' code to '='. This example will wait 3 seconds of 'hover over a link' action to take place before acting. HTH someone.
var mouseenterTime = 0;
$(document).on('mouseenter mouseleave', '#element', function (evt)
{
var currentTime = new Date();
if (evt.type === 'mouseenter')
{
mouseenterTime = currentTime.getTime();
console.log('mouseenterTime (#1): ' + mouseenterTime);
} else if (evt.type === 'mouseleave') {
console.log('mouseenterTime (#2): ' + mouseenterTime);
var mouseoverTime = currentTime.getTime() - mouseenterTime;
console.log('mouseover time was: ' + mouseoverTime);
// Checking if the Hover action has latest for longer than 3 seconds.
if(mouseoverTime > 3000) {console.log("Three seconds have elapsed")}
}
})
Is it possible to detect "idle" time in JavaScript?
My primary use case probably would be to pre-fetch or preload content.
I define idle time as a period of user inactivity or without any CPU usage
Here is a simple script using jQuery that handles mousemove and keypress events.
If the time expires, the page reloads.
<script type="text/javascript">
var idleTime = 0;
$(document).ready(function () {
// Increment the idle time counter every minute.
var idleInterval = setInterval(timerIncrement, 60000); // 1 minute
// Zero the idle timer on mouse movement.
$(this).mousemove(function (e) {
idleTime = 0;
});
$(this).keypress(function (e) {
idleTime = 0;
});
});
function timerIncrement() {
idleTime = idleTime + 1;
if (idleTime > 19) { // 20 minutes
window.location.reload();
}
}
</script>
With vanilla JavaScript:
var inactivityTime = function () {
var time;
window.onload = resetTimer;
// DOM Events
document.onmousemove = resetTimer;
document.onkeydown = resetTimer;
function logout() {
alert("You are now logged out.")
//location.href = 'logout.html'
}
function resetTimer() {
clearTimeout(time);
time = setTimeout(logout, 3000)
// 1000 milliseconds = 1 second
}
};
And initialise the function where you need it (for example: onPageLoad).
window.onload = function() {
inactivityTime();
}
You can add more DOM events if you need to. Most used are:
document.onload = resetTimer;
document.onmousemove = resetTimer;
document.onmousedown = resetTimer; // touchscreen presses
document.ontouchstart = resetTimer;
document.onclick = resetTimer; // touchpad clicks
document.onkeydown = resetTimer; // onkeypress is deprectaed
document.addEventListener('scroll', resetTimer, true); // improved; see comments
Or register desired events using an array
window.addEventListener('load', resetTimer, true);
var events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart'];
events.forEach(function(name) {
document.addEventListener(name, resetTimer, true);
});
DOM Events list: http://www.w3schools.com/jsref/dom_obj_event.asp
Remember to use window, or document according your needs. Here you can see the differences between them: What is the difference between window, screen, and document in JavaScript?
Code Updated with #frank-conijn and #daxchen improve: window.onscroll will not fire if scrolling is inside a scrollable element, because scroll events don't bubble. In window.addEventListener('scroll', resetTimer, true), the third argument tells the listener to catch the event during the capture phase instead of the bubble phase.
Improving on Equiman's (original) answer:
function idleLogout() {
var t;
window.onload = resetTimer;
window.onmousemove = resetTimer;
window.onmousedown = resetTimer; // catches touchscreen presses as well
window.ontouchstart = resetTimer; // catches touchscreen swipes as well
window.ontouchmove = resetTimer; // required by some devices
window.onclick = resetTimer; // catches touchpad clicks as well
window.onkeydown = resetTimer;
window.addEventListener('scroll', resetTimer, true); // improved; see comments
function yourFunction() {
// your function for too long inactivity goes here
// e.g. window.location.href = 'logout.php';
}
function resetTimer() {
clearTimeout(t);
t = setTimeout(yourFunction, 10000); // time is in milliseconds
}
}
idleLogout();
Apart from the improvements regarding activity detection, and the change from document to window, this script actually calls the function, rather than letting it sit idle by.
It doesn't catch zero CPU usage directly, but that is impossible, because executing a function causes CPU usage. And user inactivity eventually leads to zero CPU usage, so indirectly it does catch zero CPU usage.
I have created a small library that does this:
https://github.com/shawnmclean/Idle.js
Description:
Tiny JavaScript library to report activity of user in the browser
(away, idle, not looking at webpage, in a different tab, etc). that is independent of any
other JavaScript libraries such as jQuery.
Visual Studio users can get it from NuGet by:
Install-Package Idle.js
Here is a rough jQuery implementation of tvanfosson's idea:
$(document).ready(function(){
idleTime = 0;
//Increment the idle time counter every second.
var idleInterval = setInterval(timerIncrement, 1000);
function timerIncrement()
{
idleTime++;
if (idleTime > 2)
{
doPreload();
}
}
//Zero the idle timer on mouse movement.
$(this).mousemove(function(e){
idleTime = 0;
});
function doPreload()
{
//Preload images, etc.
}
})
Similar to Peter J's solution (with a jQuery custom event)...
// Use the jquery-idle-detect.js script below
$(window).on('idle:start', function() {
// Start your prefetch, etc. here...
});
$(window).on('idle:stop', function() {
// Stop your prefetch, etc. here...
});
File jquery-idle-detect.js
(function($, $w) {
// Expose configuration option
// Idle is triggered when no events for 2 seconds
$.idleTimeout = 2000;
// Currently in idle state
var idle = false;
// Handle to idle timer for detection
var idleTimer = null;
// Start the idle timer and bind events on load (not DOM-ready)
$w.on('load', function() {
startIdleTimer();
$w.on('focus resize mousemove keyup', startIdleTimer)
.on('blur', idleStart) // Force idle when in a different tab/window
;
]);
function startIdleTimer() {
clearTimeout(idleTimer); // Clear prior timer
if (idle) $w.trigger('idle:stop'); // If idle, send stop event
idle = false; // Not idle
var timeout = ~~$.idleTimeout; // Option to integer
if (timeout <= 100)
timeout = 100; // Minimum 100 ms
if (timeout > 300000)
timeout = 300000; // Maximum 5 minutes
idleTimer = setTimeout(idleStart, timeout); // New timer
}
function idleStart() {
if (!idle)
$w.trigger('idle:start');
idle = true;
}
}(window.jQuery, window.jQuery(window)))
You can do it more elegantly with Underscore.js and jQuery:
$('body').on("click mousemove keyup", _.debounce(function(){
// do preload here
}, 1200000)) // 20 minutes debounce
My answer was inspired by vijay's answer, but is a shorter, more general solution that I thought I'd share for anyone it might help.
(function () {
var minutes = true; // change to false if you'd rather use seconds
var interval = minutes ? 60000 : 1000;
var IDLE_TIMEOUT = 3; // 3 minutes in this example
var idleCounter = 0;
document.onmousemove = document.onkeypress = function () {
idleCounter = 0;
};
window.setInterval(function () {
if (++idleCounter >= IDLE_TIMEOUT) {
window.location.reload(); // or whatever you want to do
}
}, interval);
}());
As it currently stands, this code will execute immediately and reload your current page after 3 minutes of no mouse movement or key presses.
This utilizes plain vanilla JavaScript and an immediately-invoked function expression to handle idle timeouts in a clean and self-contained manner.
All the previous answers have an always-active mousemove handler. If the handler is jQuery, the additional processing jQuery performs can add up. Especially if the user is using a gaming mouse, as many as 500 events per second can occur.
This solution avoids handling every mousemove event. This result in a small timing error, but which you can adjust to your need.
function setIdleTimeout(millis, onIdle, onUnidle) {
var timeout = 0;
startTimer();
function startTimer() {
timeout = setTimeout(onExpires, millis);
document.addEventListener("mousemove", onActivity);
document.addEventListener("keydown", onActivity);
document.addEventListener("touchstart", onActivity);
}
function onExpires() {
timeout = 0;
onIdle();
}
function onActivity() {
if (timeout) clearTimeout(timeout);
else onUnidle();
//since the mouse is moving, we turn off our event hooks for 1 second
document.removeEventListener("mousemove", onActivity);
document.removeEventListener("keydown", onActivity);
document.removeEventListener("touchstart", onActivity);
setTimeout(startTimer, 1000);
}
}
http://jsfiddle.net/9exz43v2/
I had the same issue and I found a quite good solution.
I used jquery.idle and I only needed to do:
$(document).idle({
onIdle: function(){
alert('You did nothing for 5 seconds');
},
idle: 5000
})
See JsFiddle demo.
(Just for information: see this for back-end event tracking Leads browserload)
If you are targeting a supported browser (Chrome or Firefox as of December 2018) you can experiment with the requestIdleCallback and include the requestIdleCallback shim for unsupported browsers.
You could probably hack something together by detecting mouse movement on the body of the form and updating a global variable with the last movement time. You'd then need to have an interval timer running that periodically checks the last movement time and does something if it has been sufficiently long since the last mouse movement was detected.
I wrote a small ES6 class to detect activity and otherwise fire events on idle timeout. It covers keyboard, mouse and touch, can be activated and deactivated and has a very lean API:
const timer = new IdleTimer(() => alert('idle for 1 minute'), 1000 * 60 * 1);
timer.activate();
It does not depend on jQuery, though you might need to run it through Babel to support older browsers.
https://gist.github.com/4547ef5718fd2d31e5cdcafef0208096
(Partially inspired by the good core logic of Equiman's answer.)
sessionExpiration.js
sessionExpiration.js is lightweight yet effective and customizable. Once implemented, use in just one row:
sessionExpiration(idleMinutes, warningMinutes, logoutUrl);
Affects all tabs of the browser, not just one.
Written in pure JavaScript, with no dependencies. Fully client side.
(If so wanted.) Has warning banner and countdown clock, that is cancelled by user interaction.
Simply include the sessionExpiration.js, and call the function, with arguments [1] number of idle minutes (across all tabs) until user is logged out, [2] number of idle minutes until warning and countdown is displayed, and [3] logout url.
Put the CSS in your stylesheet. Customize it if you like. (Or skip and delete banner if you don't want it.)
If you do want the warning banner however, then you must put an empty div with ID sessExpirDiv on your page (a suggestion is putting it in the footer).
Now the user will be logged out automatically if all tabs have been inactive for the given duration.
Optional: You may provide a fourth argument (URL serverRefresh) to the function, so that a server side session timer is also refreshed when you interact with the page.
This is an example of what it looks like in action, if you don't change the CSS.
Try this code. It works perfectly.
var IDLE_TIMEOUT = 10; //seconds
var _idleSecondsCounter = 0;
document.onclick = function () {
_idleSecondsCounter = 0;
};
document.onmousemove = function () {
_idleSecondsCounter = 0;
};
document.onkeypress = function () {
_idleSecondsCounter = 0;
};
window.setInterval(CheckIdleTime, 1000);
function CheckIdleTime() {
_idleSecondsCounter++;
var oPanel = document.getElementById("SecondsUntilExpire");
if (oPanel)
oPanel.innerHTML = (IDLE_TIMEOUT - _idleSecondsCounter) + "";
if (_idleSecondsCounter >= IDLE_TIMEOUT) {
alert("Time expired!");
document.location.href = "SessionExpired.aspx";
}
}
<script type="text/javascript">
var idleTime = 0;
$(document).ready(function () {
//Increment the idle time counter every minute.
idleInterval = setInterval(timerIncrement, 60000); // 1 minute
//Zero the idle timer on mouse movement.
$('body').mousemove(function (e) {
//alert("mouse moved" + idleTime);
idleTime = 0;
});
$('body').keypress(function (e) {
//alert("keypressed" + idleTime);
idleTime = 0;
});
$('body').click(function() {
//alert("mouse moved" + idleTime);
idleTime = 0;
});
});
function timerIncrement() {
idleTime = idleTime + 1;
if (idleTime > 10) { // 10 minutes
window.location.assign("http://www.google.com");
}
}
</script>
I think this jQuery code is perfect one, though copied and modified from above answers!!
Do not forgot to include the jQuery library in your file!
Pure JavaScript with a properly set reset time and bindings via addEventListener:
(function() {
var t,
timeout = 5000;
function resetTimer() {
console.log("reset: " + new Date().toLocaleString());
if (t) {
window.clearTimeout(t);
}
t = window.setTimeout(logout, timeout);
}
function logout() {
console.log("done: " + new Date().toLocaleString());
}
resetTimer();
//And bind the events to call `resetTimer()`
["click", "mousemove", "keypress"].forEach(function(name) {
console.log(name);
document.addEventListener(name, resetTimer);
});
}());
The problem with all these solutions, although correct, is they are impractical, when taking into account the session timeout valuable set, using PHP, .NET or in the Application.cfc file for ColdFusion developers.
The time set by the above solution needs to sync with the server-side session timeout. If the two do not sync, you can run into problems that will just frustrate and confuse your users.
For example, the server side session timeout might be set to 60 minutes, but the user may believe that he/she is safe, because the JavaScript idle time capture has increased the total amount of time a user can spend on a single page. The user may have spent time filling in a long form, and then goes to submit it. The session timeout might kick in before the form submission is processed.
I tend to just give my users 180 minutes, and then use JavaScript to automatically log the user out. Essentially, using some of the code above, to create a simple timer, but without the capturing mouse event part.
In this way my client side and server-side time syncs perfectly. There is no confusion, if you show the time to the user in your UI, as it reduces. Each time a new page is accessed in the CMS, the server side session and JavaScript timer are reset. Simple and elegant. If a user stays on a single page for more than 180 minutes, I figure there is something wrong with the page, in the first place.
You can use the below mentioned solution
var idleTime;
$(document).ready(function () {
reloadPage();
$('html').bind('mousemove click mouseup mousedown keydown keypress keyup submit change mouseenter scroll resize dblclick', function () {
clearTimeout(idleTime);
reloadPage();
});
});
function reloadPage() {
clearTimeout(idleTime);
idleTime = setTimeout(function () {
location.reload();
}, 3000);
}
I wrote a simple jQuery plugin that will do what you are looking for.
https://github.com/afklondon/jquery.inactivity
$(document).inactivity( {
interval: 1000, // the timeout until the inactivity event fire [default: 3000]
mouse: true, // listen for mouse inactivity [default: true]
keyboard: false, // listen for keyboard inactivity [default: true]
touch: false, // listen for touch inactivity [default: true]
customEvents: "customEventName", // listen for custom events [default: ""]
triggerAll: true, // if set to false only the first "activity" event will be fired [default: false]
});
The script will listen for mouse, keyboard, touch and other custom events inactivity (idle) and fire global "activity" and "inactivity" events.
I have tested this code working file:
var timeout = null;
var timee = '4000'; // default time for session time out.
$(document).bind('click keyup mousemove', function(event) {
if (timeout !== null) {
clearTimeout(timeout);
}
timeout = setTimeout(function() {
timeout = null;
console.log('Document Idle since '+timee+' ms');
alert("idle window");
}, timee);
});
Is it possible to have a function run every 10 seconds, and have that check a "counter" variable? If that's possible, you can have an on mouseover for the page, can you not?
If so, use the mouseover event to reset the "counter" variable. If your function is called, and the counter is above the range that you pre-determine, then do your action.
Here is the best solution I have found:
Fire Event When User is Idle
Here is the JavaScript:
idleTimer = null;
idleState = false;
idleWait = 2000;
(function ($) {
$(document).ready(function () {
$('*').bind('mousemove keydown scroll', function () {
clearTimeout(idleTimer);
if (idleState == true) {
// Reactivated event
$("body").append("<p>Welcome Back.</p>");
}
idleState = false;
idleTimer = setTimeout(function () {
// Idle Event
$("body").append("<p>You've been idle for " + idleWait/1000 + " seconds.</p>");
idleState = true; }, idleWait);
});
$("body").trigger("mousemove");
});
}) (jQuery)
I use this approach, since you don't need to constantly reset the time when an event fires. Instead, we just record the time, and this generates the idle start point.
function idle(WAIT_FOR_MINS, cb_isIdle) {
var self = this,
idle,
ms = (WAIT_FOR_MINS || 1) * 60000,
lastDigest = new Date(),
watch;
//document.onmousemove = digest;
document.onkeypress = digest;
document.onclick = digest;
function digest() {
lastDigest = new Date();
}
// 1000 milisec = 1 sec
watch = setInterval(function() {
if (new Date() - lastDigest > ms && cb_isIdel) {
clearInterval(watch);
cb_isIdle();
}
}, 1000*60);
},
Based on the inputs provided by equiman:
class _Scheduler {
timeoutIDs;
constructor() {
this.timeoutIDs = new Map();
}
addCallback = (callback, timeLapseMS, autoRemove) => {
if (!this.timeoutIDs.has(timeLapseMS + callback)) {
let timeoutID = setTimeout(callback, timeLapseMS);
this.timeoutIDs.set(timeLapseMS + callback, timeoutID);
}
if (autoRemove !== false) {
setTimeout(
this.removeIdleTimeCallback, // Remove
10000 + timeLapseMS, // 10 secs after
callback, // the callback
timeLapseMS, // is invoked.
);
}
};
removeCallback = (callback, timeLapseMS) => {
let timeoutID = this.timeoutIDs.get(timeLapseMS + callback);
if (timeoutID) {
clearTimeout(timeoutID);
this.timeoutIDs.delete(timeLapseMS + callback);
}
};
}
class _IdleTimeScheduler extends _Scheduler {
events = [
'load',
'mousedown',
'mousemove',
'keydown',
'keyup',
'input',
'scroll',
'touchstart',
'touchend',
'touchcancel',
'touchmove',
];
callbacks;
constructor() {
super();
this.events.forEach(name => {
document.addEventListener(name, this.resetTimer, true);
});
this.callbacks = new Map();
}
addIdleTimeCallback = (callback, timeLapseMS) => {
this.addCallback(callback, timeLapseMS, false);
let callbacksArr = this.callbacks.get(timeLapseMS);
if (!callbacksArr) {
this.callbacks.set(timeLapseMS, [callback]);
} else {
if (!callbacksArr.includes(callback)) {
callbacksArr.push(callback);
}
}
};
removeIdleTimeCallback = (callback, timeLapseMS) => {
this.removeCallback(callback, timeLapseMS);
let callbacksArr = this.callbacks.get(timeLapseMS);
if (callbacksArr) {
let index = callbacksArr.indexOf(callback);
if (index !== -1) {
callbacksArr.splice(index, 1);
}
}
};
resetTimer = () => {
for (let [timeLapseMS, callbacksArr] of this.callbacks) {
callbacksArr.forEach(callback => {
// Clear the previous IDs
let timeoutID = this.timeoutIDs.get(timeLapseMS + callback);
clearTimeout(timeoutID);
// Create new timeout IDs.
timeoutID = setTimeout(callback, timeLapseMS);
this.timeoutIDs.set(timeLapseMS + callback, timeoutID);
});
}
};
}
export const Scheduler = new _Scheduler();
export const IdleTimeScheduler = new _IdleTimeScheduler();
As simple as it can get, detect when the mouse moves only:
var idle = false;
document.querySelector('body').addEventListener('mousemove', function(e) {
if(idle!=false)
idle = false;
});
var idleI = setInterval(function()
{
if(idle == 'inactive')
{
return;
}
if(idle == true)
{
idleFunction();
idle = 'inactive';
return;
}
idle = true;
}, 30000); // half the expected time. Idle will trigger after 60 s in this case.
function idleFuntion()
{
console.log('user is idle');
}
Here is an AngularJS service for accomplishing in Angular.
/* Tracks now long a user has been idle. secondsIdle can be polled
at any time to know how long user has been idle. */
fuelServices.factory('idleChecker',['$interval', function($interval){
var self = {
secondsIdle: 0,
init: function(){
$(document).mousemove(function (e) {
self.secondsIdle = 0;
});
$(document).keypress(function (e) {
self.secondsIdle = 0;
});
$interval(function(){
self.secondsIdle += 1;
}, 1000)
}
}
return self;
}]);
Keep in mind this idle checker will run for all routes, so it should be initialized in .run() on load of the angular app. Then you can use idleChecker.secondsIdle inside each route.
myApp.run(['idleChecker',function(idleChecker){
idleChecker.init();
}]);
Surely you want to know about window.requestIdleCallback(), which queues a function to be called during a browser's idle periods.
You can see an elegant usage of this API in the Quicklink repo.
const requestIdleCallback = window.requestIdleCallback ||
function (cb) {
const start = Date.now();
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
},
});
}, 1);
};
The meaning of the code above is: if the browser supports requestIdleCallback (check the compatibility), uses it. If is not supported, uses a setTimeout(()=> {}, 1) as fallback, which should queue the function to be called at the end of the event loop.
Then you can use it like this:
requestIdleCallback(() => {...}, {
timeout: 2000
});
The second parameter is optional, you might want to set a timeout if you want to make sure the function is executed.
You could probably detect inactivity on your web page using the mousemove tricks listed, but that won't tell you that the user isn't on another page in another window or tab, or that the user is in Word or Photoshop, or WoW and just isn't looking at your page at this time.
Generally, I'd just do the prefetch and rely on the client's multi-tasking. If you really need this functionality, you do something with an ActiveX control in Windows, but it's ugly at best.
Debounce is actually a great idea! Here is a version for jQuery-free projects:
const derivedLogout = createDerivedLogout(30);
derivedLogout(); // It could happen that the user is too idle)
window.addEventListener('click', derivedLogout, false);
window.addEventListener('mousemove', derivedLogout, false);
window.addEventListener('keyup', derivedLogout, false);
function createDerivedLogout (sessionTimeoutInMinutes) {
return _.debounce( () => {
window.location = this.logoutUrl;
}, sessionTimeoutInMinutes * 60 * 1000 )
}