Как элегантно определить простой в JavaScript? - PullRequest
424 голосов
/ 20 марта 2009

Можно ли определить время " простоя " в JavaScript?
Вероятно, мой основной вариант использования - предварительная загрузка или предварительная загрузка содержимого.

Время простоя: Период бездействия пользователя или без использования процессора

Ответы [ 35 ]

2 голосов
/ 03 января 2014

Я проверил этот код рабочего файла:

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);
    });
2 голосов
/ 02 декабря 2016

Я использую этот подход, поскольку вам не нужно постоянно сбрасывать время, когда происходит событие, вместо этого мы просто записываем время, это генерирует начальную точку холостого хода.

           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);    
        },
1 голос
/ 23 октября 2015

Вот служба AngularJS для выполнения в 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;
}]);

Имейте в виду, что эта программа проверки на холостом ходу будет работать для всех маршрутов, поэтому она должна быть инициализирована в .run() при загрузке углового приложения. Тогда вы можете использовать idleChecker.secondsIdle внутри каждого маршрута.

myApp.run(['idleChecker',function(idleChecker){
    idleChecker.init();
}]);
1 голос
/ 20 марта 2009

Вы, вероятно, можете обнаружить неактивность на своей веб-странице, используя перечисленные приемы перемещения мышью, но это не скажет вам, что пользователь не находится на другой странице в другом окне или вкладке или что пользователь находится в Word или Photoshop или WOW и просто не смотрит на вашу страницу в это время. Обычно я просто делаю предварительную выборку и полагаюсь на многозадачность клиента. Если вам действительно нужна эта функциональность, вы делаете что-то с элементом управления activex в windows, но в лучшем случае это ужасно.

1 голос
/ 18 мая 2013

Для других пользователей с такой же проблемой. Вот функция, которую я только что составил.

Он НЕ запускается при каждом движении мыши или сбрасывает таймер при каждом движении мыши.

<script>
// Timeout in seconds
var timeout = 10; // 10 seconds

// You don't have to change anything below this line, except maybe
// the alert('Welcome back!') :-)
// ----------------------------------------------------------------
var pos = '', prevpos = '', timer = 0, interval = timeout / 5 * 1000;
timeout = timeout * 1000 - interval;
function mouseHasMoved(e){
    document.onmousemove = null;
    prevpos = pos;
    pos = e.pageX + '+' + e.pageY;
    if(timer > timeout){
        timer = 0;
        alert('Welcome back!');
    }
}
setInterval(function(){
    if(pos == prevpos){
        timer += interval;
    }else{
        timer = 0;
        prevpos = pos;
    }
    document.onmousemove = function(e){
        mouseHasMoved(e);
    }
}, interval);
</script>
1 голос
/ 20 марта 2009

Всего несколько мыслей, проспект или два, чтобы исследовать.

Можно ли запускать функцию каждые 10 секунд и проверять переменную "counter"? Если это возможно, вы можете навести курсор мыши на страницу, не так ли? Если это так, используйте событие mouseover для сброса переменной «counter». Если ваша функция вызывается, а счетчик находится выше предопределенного диапазона, выполните свое действие.

Опять, только некоторые мысли ... Надеюсь, это поможет.

1 голос
/ 08 января 2019

Как можно проще определить, только когда мышь двигается:

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 60s in this case.

function idleFuntion()
{
   console.log('user is idle');
}
1 голос
/ 03 июля 2018

(Частично вдохновлен хорошей базовой логикой Equiman ранее в этой теме.)

sessionExpiration.js


sessionExpiration.js легкий, но эффективный и настраиваемый. После внедрения используйте только одну строку:

sessionExpiration(idleMinutes, warningMinutes, logoutUrl);
  • Влияет на все вкладки браузера, а не только на одну.
  • Написано на чистом JavaScript , без зависимостей. Полностью клиентская сторона.
  • (При желании.) Имеет предупреждающий баннер и обратный отсчет часов, которые отменяются при взаимодействии с пользователем.
  • Просто включите sessionExpiration.js и вызовите функцию с аргументами [1] количество минут простоя (на всех вкладках) до выхода пользователя из системы, [2] количество минут простоя, пока не отобразятся предупреждение и обратный отсчет, и [3] URL выхода из системы.
  • Поместите CSS в вашу таблицу стилей. Настройте его, если хотите. (Или пропустите и удалите баннер, если он вам не нужен.)
  • Если вы действительно хотите получить предупреждение, однако, вы должны поместить пустой div с идентификатором sessExpirDiv на свою страницу (есть предложение поместить его в нижний колонтитул) .
  • Теперь пользователь автоматически выйдет из системы, если все вкладки были неактивны в течение заданного времени.

Это пример того, как это выглядит в действии, если вы не измените CSS.

demo_image

0 голосов
/ 04 мая 2019

Вы попросили элегантности, и я создал простой класс, который также поддерживает ленивую проверку (которая имеет состояние ожидания), кроме императивного способа (с обратными вызовами). Кроме того, этот класс поддерживает "backToActive", когда нарушается время простоя.

class Idle {
    constructor(timeout = 10, idleCallback = null, backToActiveCallback = null, autoStart = true, backToActiveOnXHR = false) {
        this.timeout = timeout
        this.idleCallback = idleCallback
        this.backToActiveCallback = backToActiveCallback
        this.autoStart = autoStart // only F5
        this.backToActiveOnXHR = backToActiveOnXHR
        this.idle = false
        this.timer = null
        this.events = ['scroll', 'mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart']
        this.init()
    }

    init() {
        if(this.backToActiveOnXHR) {
            this.events.push('load')
        }
        this.events.forEach(name => {
            window.addEventListener(name, this.backToActive, true)
        })
        if(this.autoStart) {
            this.backToActive()
        }
    }

    goIdle = () => {
        this.idle = true
        if(!!this.idleCallback) {
            this.idleCallback(this.timeout)
        }
    }

    backToActive = () => {
        if(this.idle) {
            this.backToActiveCallback()
        }
        this.idle = false
        clearTimeout(this.timer)
        this.timer = setTimeout(this.goIdle, this.timeout * 1000)
    }
}

Использование:

let idleCallback = timeout => { console.log(`Went idle after ${timeout} seconds`) }
let backToActiveCallback = () => { console.log('Back to active') }
let idle = new Idle(30, idleCallback, backToActiveCallback)

Результат в devtools:

// Went idle after 30 seconds <--- goes idle when no activity is detected
// Back to active <--- when the user is detected again

Преимущество поддержки лени:

setInterval(() => {
    common.fetchApi('/api/v1/list', { status: idle.idle ? 'away' : 'online' }).then(/* show a list of elements */)
}, 1000 * 5)

Зачем вам ленивый чек? Иногда мы используем периодический XHR (с setInterval), то есть когда пользователь просматривает список рейсов, поездок, фильмов, заказов и т. Д. С каждым XHR мы затем можем добавлять информацию о его статусе активности (онлайн / нет), поэтому у нас есть Чувство активных пользователей в нашей системе.

Мой класс основан на ответах Эквимана и Фрэнка Конейна.

0 голосов
/ 25 октября 2018

Реализация, которую я предлагаю здесь, отличается от других ответов следующими способами:

  • событие ожидания (по умолчанию называется idleTimeSeconds) запускается каждые 10 секунд, поэтому вы можете иметь несколько подписчиков на одно и то же событие
  • для каждого экземпляра документа установлен только один таймер
  • таймер срабатывает чаще, чем событие простоя (по умолчанию каждую 1 секунду против каждых 10 секунд) - это обеспечит точность интервала по умолчанию
  • отметка времени начала простоя записывается и используется для расчета общего времени простоя; другие решения предлагают постепенно добавлять секунды к счетчику времени простоя, что является меньшими ценами, поскольку фактическая задержка таймера может быть дольше, чем настроено, см. «Причины задержек, превышающих заданные в WindowOrWorkerGlobalScope.setTimeout ()» для примеров.
  • таймер никогда не отменяется / не сбрасывается, как предлагают некоторые другие решения; отмена и сброс таймеров дороже

Файл 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_TIMER_RATE_SECONDS в Idle.js).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...