【问题标题】:How to detect idle time in JavaScript如何在 JavaScript 中检测空闲时间
【发布时间】:2021-08-23 09:32:23
【问题描述】:

是否可以在 JavaScript 中检测“idle”时间?

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

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

【问题讨论】:

  • 如果有人有关于 CPU 空闲的答案,我会非常感兴趣。在大多数情况下,用户交互不会非常空闲。

标签: javascript


【解决方案1】:

这是一个使用 jQuery 处理 mousemove 和 keypress 事件的简单脚本。 如果时间到期,页面会重新加载。

<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>

【讨论】:

  • 您在 $(document).ready(function() 主体之后缺少分号。此外,在对 setInterval 的调用中,它不适用于函数名称周围的引号,并且您不要'不需要后面的括号。只需:setInterval(timerIncrement, 60000)
  • @Jesse:你的建议都很好,代码应该是这样的。但我只想指出,即使没有这些更改,代码也是完全正常的。表达式语句末尾的分号是可选的,实际上您可以将字符串传递给 setInterval,然后将其计算为 JavaScript。
  • 您可以简单地使用idleTime++; 而不是idleTime = idleTime + 1;
  • 这对用户的系统来说不是很重吗?比方说,一个用户在一台不太重的电脑上使用相当旧的浏览器,在一个 javascript 应用程序中工作了半天,每次用户移动鼠标时它都会处理这些函数......我想知道这是否会'不会影响用户体验...
  • @PietBinnenbocht 另外,如果你开始优化这样的东西,你也可以改变每个接受像'mousemove keydown click' 这样的字符串的函数来使用位标志(Event.MOUSEMOVE | Event.KEYDOWN | Event.CLICK),因为它们比字符串操作。但是你真的想要这样做吗?
【解决方案2】:

不使用 jQuery,只使用原生 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
    }
};

并在你需要的地方初始化函数(例如:onPageLoad)。

window.onload = function() {
  inactivityTime();
}

如果需要,您可以添加更多 DOM 事件。最常用的是:

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

或者使用数组注册想要的事件

window.addEventListener('load', resetTimer, true);
var events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart'];
events.forEach(function(name) {
 document.addEventListener(name, resetTimer, true);
});

DOM 事件 列表:http://www.w3schools.com/jsref/dom_obj_event.asp

记得根据需要使用window,或document。在这里你可以看到它们之间的区别:What is the difference between window, screen, and document in JavaScript?

使用@frank-conijn 和@daxchen 改进

代码更新:如果滚动在可滚动元素内,window.onscroll 将不会触发,因为滚动事件不会冒泡。在window.addEventListener('scroll', resetTimer, true) 中,第三个参数告诉监听器在捕获阶段而不是冒泡阶段捕获事件。

【讨论】:

  • 绝对重置计时器是比让超时做它的事情更直接/直观和准确的方法,只是为了保持另一个事情的整数计数,当计时器本身可以是(高精度) 计数器。
  • @mpsbhat 只需添加一个 console.log 或一个警报,看看是否有效。或注册此事件:document.onload = function () { inactivityTime(); }; document.onmousedown = function () { inactivityTime(); }; document.onkeypress = function () { inactivityTime(); }; document.ontouchstart = function () { inactivityTime(); };
  • 是的...工作。 jsfiddle.net/mpsbhat/6b6mja5t/1。谢谢@equiman
  • 最好有一个标志 var notidle;仅在事件上设置 flag = true。然后在resetTimer函数中测试notidle标志是否为真,如果是reset timer,或者调用logout。这将消除不断重置计时器的复杂性开销。
  • 简单/优雅的解决方案我刚刚更新并添加了超时作为参数,例如:var inactivityTime = function (timeout) { 所以我们可以在外部控制它
【解决方案3】:

改进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();

除了关于活动检测的改进以及从documentwindow 的更改之外,此脚本实际上调用了该函数,而不是让它闲置。

它不会直接捕获零 CPU 使用率,但这是不可能的,因为执行函数会导致 CPU 使用率。并且用户不活动最终导致 CPU 使用率为零,因此它间接地捕获了 CPU 使用率为零。

【讨论】:

  • 只是想指出window.onscroll 在可滚动元素内滚动时不会触发,因为滚动事件不会冒泡。使用window.addEventListener('scroll', resetTimer, true),第三个参数告诉侦听器在capture 阶段而不是bubble 阶段(IE > 8)捕获事件,see this answer
  • @DaxChen -- document.onscroll 不是有同样的问题,如果滚动是在可滚动的子对象内部,则不会触发?
  • 是的,我的意思是使用addEventListener 而不是onscroll
  • 我将用这些重要信息更新我的答案,以防止其他人复制并粘贴我的错误。谢谢@DaxChen 和 Frank
  • 顺便说一句 onkeypress is depricated 使用 onkeydown
【解决方案4】:

我创建了一个小型库来执行此操作:

https://github.com/shawnmclean/Idle.js

说明:

用于报告用户在浏览器中的活动的小型 JavaScript 库 (离开,闲置,不看网页,在不同的标签等)。独立于任何 其他 JavaScript 库,例如 jQuery。

Visual Studio 用户可以通过以下方式从 NuGet 获取它:

Install-Package Idle.js

【讨论】:

    【解决方案5】:

    这是tvanfosson's idea的粗略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.
       }
       
    })
    

    【讨论】:

    • 此解决方案不考虑键盘事件。
    • 永远不要传递setInterval 一个字符串!只要给一个函数作为变量!
    • 这实际上不起作用,因为将字符串传递给 setInterval() 会在全局范围内评估表达式,因此它不会找到 .ready 处理函数内的 timerIncrement() 函数。这是永远不要将字符串传递给setInterval() 的另一个原因。只需传递一个实际的函数引用,您就不会遇到这个问题,因为它们是在当前范围内评估的。
    • 谢谢,我不知道永远不要将字符串传递给 setInterval。更新了我的答案。
    【解决方案6】:

    类似于Peter J's solution(带有 jQ​​uery 自定义事件)...

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

    文件 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)))
    

    【讨论】:

      【解决方案7】:

      您可以使用Underscore.jsjQuery 更优雅地做到这一点:

      $('body').on("click mousemove keyup", _.debounce(function(){
          // do preload here
      }, 1200000)) // 20 minutes debounce
      

      【讨论】:

        【解决方案8】:

        我的回答受到vijay's answer 的启发,但它是一个更简短、更通用的解决方案,我认为我会分享给任何可能有帮助的人。

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

        就目前而言,此代码将立即执行,并在 3 分钟没有鼠标移动或按键后重新加载您的当前页面。

        这使用普通的 JavaScript 和 immediately-invoked function expression 以干净且独立的方式处理空闲超时。

        【讨论】:

        • document.onclick 考虑使用 .trigger('click') 的 javascript 函数,我将其编写为自动化。所以这不是真正的用户交互,但在这种情况下它会重置 idleCounter
        • @Carmela:我不知道为什么我只是看到这个;我一定错过了。谢谢,我删除了onclick 分配,因为除了onmousemove 之外它可能没有必要,但显然这些以编程方式触发的任何事件都将继续重置idleCounter。我不确定你为什么要模拟用户交互而不是仅仅调用一个函数,但如果这是你出于某种原因需要做的事情,这个答案显然对你不起作用,我的大多数其他答案也不会我看过这个问题。
        【解决方案9】:

        所有以前的答案都有一个始终处于活动状态的 mousemove 处理程序。如果处理程序是 jQuery,则 jQuery 执行的附加处理可以累加。尤其是当用户使用游戏鼠标时,每秒可能发生多达 500 个事件。

        此解决方案避免处理每个 mousemove 事件。这会导致一个小的计时错误,但您可以根据需要进行调整。

        function setIdleTimeout(millis, onIdle, onUnidle) {
            var timeout = 0;
            startTimer();
        
            function startTimer() {
                timeout = setTimeout(onExpires, millis);
                document.addEventListener("mousemove", onActivity);
                document.addEventListener("keydown", 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);
                setTimeout(startTimer, 1000);
            }
        }
        

        http://jsfiddle.net/jndxq51o/

        【讨论】:

        • 如果放置在页面上,它会自动工作,还是需要在 $(document).ready() 包装器中?谢谢!另外,定时器到期时执行动作的部分在哪里?
        • 您可以随时调用它,甚至在文档准备好之前。您传递了一个函数“回调”,该函数将在计时器到期时调用。
        • $(startTimer) 等价于$(document).ready(startTimer),确保 DOM 在您挂钩 mousemove 和 keypress 事件之前准备就绪。
        • +1 这就是我所做的 - mousemove 处理程序会导致反应迟缓并缩短电池寿命,因此如果您能承受轻微的计时错误,那么仅定期打开它是一个好主意。我通常使用空闲时间检测来自动发出会话到期警告(例如“你还在吗?”),所以我倾向于在用户“空闲”之前有很多分钟,在这种情况下,一个小的计时错误完全无关紧要。跨度>
        • 使用“keydown”比使用“keypress”更好,因为“keypress”事件不会检测到箭头键。因此,如果用户使用箭头键导航页面,无论如何它都会变为空闲。
        【解决方案10】:

        我遇到了同样的问题,我找到了一个很好的解决方案。

        我使用了jquery.idle,我只需要这样做:

        $(document).idle({
          onIdle: function(){
            alert('You did nothing for 5 seconds');
          },
          idle: 5000
        })
        

        JsFiddle demo

        (仅供参考:see this for back-end event tracking Leads browserload

        【讨论】:

        • 我怎样才能停止这个功能,他们声明了一个事件 idle:stop 但老实说我不知道​​如何使用它。我希望如果我移动到下一页(基于 ajax,所以只更新了 HTML 页面的片段)然后空闲功能停止。您知道如何实现这一目标吗?
        • Here 它说:“idle:stop”:将停止并删除用户跟踪
        • 我已经阅读了但不知道如何使用这个,你能帮我吗?
        • 如果您希望它只触发一次,您可以将keepTracking 选项设置为false。如果您想重置,您可以尝试重新初始化。这是一个只触发一次的修改示例:jsfiddle.net/f238hchm/12
        • 不,我不会触发一次,keepTracking 应该为真,但在导航到其他页面时我想停止此操作
        【解决方案11】:

        您可以通过检测表单主体上的鼠标移动并使用上次移动时间更新全局变量来组合一些东西。然后,您需要运行一个间隔计时器,它会定期检查最后一次移动时间,并在检测到最后一次鼠标移动后的时间足够长时执行某些操作。

        【讨论】:

        • 重要的是要注意,脚本只能检测页面主体上的运动,而不是所有用户输入。我认为没有办法从 javascript 获取 CPU 或进程信息。
        • 我冒昧地在 jQuery 中实现了你的想法。
        【解决方案12】:

        如果您的目标是 supported browser(Chrome 或 Firefox,截至 2018 年 12 月),您可以尝试使用 requestIdleCallback,并为不受支持的浏览器添加 requestIdleCallback shim

        【讨论】:

        • 不理会其他浏览器?
        • Shim 覆盖所有浏览器。这是唯一的答案!其他减慢答案是关于鼠标的。交互性或 CPU 仅作为示例。如何用鼠标解决CPU空闲检测?
        • 这应该是唯一被接受的答案。如果你正在做的工作可能会延迟主线程,它可能会导致动画卡顿等,所以你应该在支持的地方使用requestIdleCallback(或requestAnimationFrame)。
        【解决方案13】:

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

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

        它确实依赖于 jQuery,但您可能需要通过 Babel 运行它以支持旧版浏览器。

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

        【讨论】:

          【解决方案14】:

          试试这个代码。效果很好。

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

          【讨论】:

          • 解释一下。例如,为什么它可以完美运行?想法/要点是什么?与之前的答案有何不同?
          【解决方案15】:

          (部分灵感来自Equiman's answer 的良好核心逻辑。)

          sessionExpiration.js


          sessionExpiration.js 是轻量级但有效且可定制的。实施后,仅在一行中使用:

          sessionExpiration(idleMinutes, warningMinutes, logoutUrl);
          
          • 影响浏览器的所有标签,而不仅仅是一个。
          • 纯 JavaScript 编写,没有依赖关系。完全客户端。
          • (如果需要。)具有警告横幅倒计时时钟,可通过用户交互取消。
          • 只需包含 sessionExpiration.js,并使用参数 [1] 调用该函数,直到用户注销之前的空闲分钟数(跨所有选项卡),[2] 显示警告和倒计时之前的空闲分钟数,以及[3] 注销网址。
          • 将 CSS 放入样式表中。如果您愿意,可以自定义它。 (如果您不想要,也可以跳过并删除横幅。)
          • 如果您确实想要警告横幅,那么您必须在页面上放置一个 ID 为 sessExpirDiv 的空 div(建议将其放在页脚)
          • 现在,如果所有选项卡在给定的持续时间内都处于非活动状态,用户将自动注销。
          • 可选:您可以为函数提供第四个参数(URL serverRefresh),以便在您与页面交互时也刷新服务器端会话计时器。

          如果您不更改 CSS,这是一个实际效果示例。

          【讨论】:

            【解决方案16】:
            <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>
            

            我认为这个 jQuery 代码是完美的,虽然是从上面的答案复制和修改的!!

            不要忘记在文件中包含 jQuery 库!

            【讨论】:

            • 这是我最终使用的版本。我只是将我的 window.location 指向一个可以取消设置会话变量并显示注销消息的路由。
            【解决方案17】:

            通过addEventListener 正确设置重置时间和绑定的纯 JavaScript:

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

            【讨论】:

              【解决方案18】:

              所有这些解决方案的问题,虽然是正确的,但它们是不切实际的,当考虑到会话超时有价值的设置时,使用 PHP、.NET 或在 ColdFusion 开发人员的 Application.cfc 文件中。

              上述方案设置的时间需要与服务器端会话超时时间同步。如果两者不同步,您可能会遇到只会让您的用户感到沮丧和困惑的问题。

              例如,服务器端会话超时可能设置为 60 分钟,但用户可能认为他/她是安全的,因为 JavaScript 空闲时间捕获增加了用户可以在单次上花费的总时间页。用户可能已经花时间填写了一个长表单,然后去提交它。会话超时可能会在处理表单提交之前启动。

              我倾向于只给我的用户 180 分钟,然后使用 JavaScript 自动将用户注销。本质上,使用上面的一些代码来创建一个简单的计时器,但没有捕获鼠标事件部分。

              通过这种方式,我的客户端和服务器端时间完美同步。如果您在 UI 中向用户显示时间,则不会造成混淆,因为它会减少。每次在 CMS 中访问新页面时,都会重置服务器端会话和 JavaScript 计时器。简单而优雅。如果用户在一个页面上停留超过 180 分钟,我首先认为该页面有问题。

              【讨论】:

              • 是的,这就是为什么我只是在摆脱服务器端会话并从 html 文件加载所有内容之后才这样做。
              【解决方案19】:

              您可以使用下面提到的解决方案

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

              【讨论】:

                【解决方案20】:

                我编写了一个简单的 jQuery 插件,可以满足您的需求。

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

                该脚本将侦听鼠标、键盘、触摸和其他自定义事件不活动(空闲)并触发全局“活动”和“不活动”事件。

                【讨论】:

                • 真的需要延迟吗,从自定义事件处理程序触发自定义事件还不够吗?
                【解决方案21】:

                我已经测试过这个代码工作文件:

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

                【讨论】:

                  【解决方案22】:

                  是否可以让函数每 10 秒运行一次,并检查“计数器”变量?如果可能的话,您可以在页面上设置鼠标悬停,不是吗?

                  如果是这样,请使用鼠标悬停事件来重置“计数器”变量。如果您的函数被调用,并且计数器高于您预先确定的范围,则执行您的操作。

                  【讨论】:

                    【解决方案23】:

                    这是我找到的最佳解决方案:

                    Fire Event When User is Idle

                    这里是 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)
                    

                    【讨论】:

                      【解决方案24】:

                      我使用这种方法,因为您不需要不断地重置事件触发的时间。相反,我们只记录时间,这会生成空闲起点。

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

                      【讨论】:

                        【解决方案25】:

                        基于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();
                        

                        【讨论】:

                        • 提供一个如何使用它的例子会很好。
                        【解决方案26】:

                        尽可能简单,只检测鼠标何时移动:

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

                        【讨论】:

                        • “只移动”是什么意思?与点击相反?还是别的什么?
                        【解决方案27】:

                        这是一个 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;
                        }]);
                        

                        请记住,这个空闲检查器将对所有路由运行,因此它应该在加载 Angular 应用程序时在 .run() 中初始化。然后你可以在每条路由中使用idleChecker.secondsIdle

                        myApp.run(['idleChecker',function(idleChecker){
                            idleChecker.init();
                        }]);
                        

                        【讨论】:

                          【解决方案28】:

                          您肯定想知道window.requestIdleCallback(),它在浏览器空闲期间将要调用的函数排队。

                          您可以在Quicklink repo 中看到此 API 的优雅用法。

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

                          上面代码的意思是:如果浏览器支持requestIdleCallback(检查兼容性),就使用它。如果不支持,则使用setTimeout(()=&gt; {}, 1) 作为回退,它应该将要在事件循环结束时调用的函数排队。

                          那么你可以这样使用它:

                          requestIdleCallback(() => {...}, {
                              timeout: 2000
                            });
                          

                          第二个参数是可选的,如果你想确保函数被执行,你可能需要设置一个timeout

                          【讨论】:

                            【解决方案29】:

                            您可能会使用列出的 mousemove 技巧检测网页上的非活动状态,但这不会告诉您用户不在另一个窗口或选项卡的另一个页面上,或者用户在 WordPhotoshopWoW,只是此时没有查看您的页面。

                            一般来说,我只是进行预取并依赖客户端的多任务处理。如果你真的需要这个功能,你可以在 Windows 中使用ActiveX 控件来做一些事情,但它充其量是丑陋的。

                            【讨论】:

                              【解决方案30】:

                              去抖动实际上是个好主意!这是一个无 jQuery 项目的版本:

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

                              【讨论】:

                                猜你喜欢
                                • 2010-10-14
                                • 2012-09-20
                                • 1970-01-01
                                • 2010-10-29
                                • 2010-11-13
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多