是否有可能在JavaScript中检测“空闲”时间?

我的主要用例可能是预取或预加载内容。

我将空闲时间定义为用户不活动或没有任何CPU使用的时间段


当前回答

试试这段代码。它工作得很完美。

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";
    }
}

其他回答

我在这里提出的实现方法与其他答案在以下方面有所不同:

the idle event (by default named 'idleTimeSeconds') is fired every 10 seconds, so you can have multiple subscribers to the same event there is only one timer set per the document instance the timer is fired more often then the idle event (by default every 1 second vs every 10 seconds) - this will make for the default interval precision the timestamp of when the idle time started is recorded and is used to calculate the total idle time; other solutions propose to incrementally add seconds to the idle time counter, which is less prices because the actual delay of a timer may be longer than configured, see "Reasons for delays longer than specified in WindowOrWorkerGlobalScope.setTimeout()" for examples. the timer is never cancelled / reset, as proposed by some other solutions; cancelling and resetting timers is more expensive

文件Idle.js:

import $ from 'jquery';

export const IDLE_EVENT_NAME = 'idleTimeSeconds';

/**
 * How often an 'idleTimeSeconds' event is fired on the document instance.
 *
 * @type {number}
 */
const IDLE_EVENT_RATE_SECONDS = 10;

/**
 * How often the idle time is checked against the IDLE_EVENT_RATE_SECONDS.
 *
 * Should be much smaller than the value of IDLE_EVENT_RATE_SECONDS
 * (the smaller the value is, the more precisely the event is fired) -
 * because the actual delay may be longer, see "Reasons for delays
 * longer than specified in WindowOrWorkerGlobalScope.setTimeout() for examples":
 * https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Reasons_for_delays_longer_than_specified
 *
 * @type {number}
 */
const IDLE_TIMER_RATE_SECONDS = 1;

/**
 * Because the actual timer delay may be longer, we track the timestamp
 * when the idle time started, instead of incrementally adding to the total idle time.
 * Having a starting point, we can always calculate the idle time precisely
 * without accumulating delay errors.
 *
 * @type {number}
 */
let idleStartTimeMilliseconds;

/**
 * Holds the interval reference.
 */
let idleInterval;

/**
 * Holds the value of the latest idle time value
 * for which the event was fired (integer value in seconds).
 *
 * The value is therefore factor of IDLE_EVENT_RATE_SECONDS.
 *
 * @type {number}
 */
let lastFiredSeconds;

const $document = $(document);

/**
 * Resets the idle timer.
 * Called on user interaction events, like keydown or touchstart.
 */
function resetIdleStartTime() {

    // Reset the timestamp when the idle time started
    idleStartTimeMilliseconds = (new Date).getTime();

    // Reset the latest idle time value for which the even was fired
    // (integer value in seconds).
    lastFiredSeconds = 0;
}

/**
 * Ticks every IDLE_TIMER_RATE_SECONDS, which is more often than the expected
 * idle event firing rate.
 *
 * Fires the 'idleTimeSeconds' event on the document instance.
 */
function timerCallback() {

    const nowMilliseconds = (new Date).getTime();
    const idleTimeSeconds = Math.floor((nowMilliseconds - idleStartTimeMilliseconds) / 1000);

    // When do we expect the idle event to be fired again?
    // For example, if the event firing rate is 10 seconds,
    // and last time it was fired at 40 seconds of idle time,
    // the next one will be at 40 + 10 = 50 seconds.
    const nextIdleSecondsToFire = lastFiredSeconds + IDLE_EVENT_RATE_SECONDS;

    if (idleTimeSeconds >= nextIdleSecondsToFire) {

        // Record last fired idle time that is factor of the rate,
        // so that we keep firing the event as close to the desired rate as possible
        lastFiredSeconds = nextIdleSecondsToFire;

        $document.triggerHandler(IDLE_EVENT_NAME, [idleTimeSeconds]);
    }
}

// Initialize the idle timer once only per the document instance
$(function() {

    // Start the idle timer
    idleInterval = setInterval(timerCallback, IDLE_TIMER_RATE_SECONDS * 1000);

    // Reset the idle time start timestamp
    $document.on('mousemove keydown mousedown touchstart', resetIdleStartTime);
});

示例用法(例如文件index.js):

import {IDLE_EVENT_NAME} from './Idle';
import $ from 'jquery';

$(function() {
    $(document).on(IDLE_EVENT_NAME, function(e, idleSeconds) {
        console.log('IDLE SECONDS:', idleSeconds);
    });
});

示例输出(节选):

IDLE SECONDS: 580
IDLE SECONDS: 590
IDLE SECONDS: 600
IDLE SECONDS: 610
IDLE SECONDS: 620
IDLE SECONDS: 630
IDLE SECONDS: 640
IDLE SECONDS: 650
IDLE SECONDS: 660
IDLE SECONDS: 670
IDLE SECONDS: 680
IDLE SECONDS: 691
IDLE SECONDS: 700
IDLE SECONDS: 710
IDLE SECONDS: 720
IDLE SECONDS: 730
IDLE SECONDS: 740
IDLE SECONDS: 750
IDLE SECONDS: 761
IDLE SECONDS: 770
IDLE SECONDS: 780
IDLE SECONDS: 790
IDLE SECONDS: 800
IDLE SECONDS: 810
IDLE SECONDS: 820
IDLE SECONDS: 830
IDLE SECONDS: 840
IDLE SECONDS: 850
IDLE SECONDS: 860
IDLE SECONDS: 871
IDLE SECONDS: 880
IDLE SECONDS: 890
IDLE SECONDS: 900
IDLE SECONDS: 910
IDLE SECONDS: 921

上面的输出是当我切换到另一个选项卡并在那里做一些活动时产生的。可以看到,计时器有时会延迟(我想是因为在后台选项卡中,计时器以精确的速率被触发不是优先级)。但是空闲计时器仍然以正确的+/- 1秒的间隔触发。在这种情况下,1秒是空闲计时器的精度(通过idle .js中的IDLE_TIMER_RATE_SECONDS常量配置)。

我编写了一个小型ES6类来检测活动,并在空闲超时时触发事件。它涵盖了键盘,鼠标和触摸,可以激活和禁用,并且有一个非常精简的API:

const timer = new IdleTimer(() => alert('idle for 1 minute'), 1000 * 60 * 1);
timer.activate();

它不依赖于jQuery,不过您可能需要通过Babel来运行它以支持旧的浏览器。

https://gist.github.com/4547ef5718fd2d31e5cdcafef0208096

我已经测试了这段代码工作文件:

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);
    });

您可能可以通过检测窗体主体上的鼠标移动并使用最后的移动时间更新全局变量来拼凑一些东西。然后,您需要运行一个间隔计时器,定期检查最后一次移动时间,如果距离检测到最后一次鼠标移动已经足够长,则执行一些操作。

下面是tvanfosson的想法的粗略jQuery实现:

$(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.
   }
   
})