如何使用JavaScript/jQuery检测用户当前是否正在页面上活跃?

45

我需要检测用户是否在当前页面上超过30分钟没有活动(点击或输入)。

我认为最好使用附加到body标签的事件冒泡,然后只需重置一个30分钟的计时器,但我不确定如何创建它。

我可以使用jQuery,但我不确定要使用多少jQuery。

编辑: 我更需要知道他们是否在积极使用该站点,因此点击(更改字段或位置、选择复选框/单选按钮)或键入(在输入框,文本区域等中)。如果他们在另一个标签页中或正在使用其他程序,则我的假设是他们没有使用该站点,因此出于安全原因应注销他们。

编辑#2: 所有人都清楚,这与确定用户是否已登录,已验证或其他完全无关。现在,如果用户在30分钟内没有发出页面请求,服务器将注销该用户。此功能可防止有人花费> 30分钟填写表格,然后提交表格,结果发现他们已经被注销。因此,将与服务器站点一起使用此功能来确定用户是否处于不活动状态(未点击或输入)。基本上,如果空闲时间超过25分钟,他们将被提示输入密码对话框。如果他们在5分钟内没有回应,则系统会自动注销他们以及服务器的会话(与大多数站点一样,下次访问页面时)。

JavaScript仅用作向用户发出警告。如果禁用JavaScript,则他们将无法收到警告,并且(与大部分网站不起作用一样),他们将在下次请求新页面时注销。


1
这是完全必要的。是的,这很烦人,但由于我们正在处理紧急响应数据(例如,天然气管道爆炸),我们需要保护数据的安全性,而不幸的是用户并不总是在安全的机器上(位置方面)。我们不能让人们坐下来使用别人的帐户。 - Darryl Hein
2
好的,但不要忘记JS对于大多数安全任务来说并不可靠,因为几乎所有浏览器都有一种机制可以简单地禁用它。你真的需要想出一个最长停留在页面上的时间,然后每次加载页面时,在某个地方存储时间戳,然后在执行任何操作之前,应该先检查服务器端的时间戳。 - Alex S
@Darryl Hein,密码不应该简单地发布,通过 SSL 发送!明文是不安全的! - Malfist
1
好的...是的,我正在通过SSL发送密码。哈哈。我只是在询问如何超时用户并检测他们是否在活跃使用该网站。服务器也会检查以确保他们仍然登录--它仍然会执行完整的身份验证过程。 - Darryl Hein
1
再次强调,这仅仅是为了确定它们是否处于空闲状态。与实际身份验证无关。 - Darryl Hein
显示剩余7条评论
11个回答

35

这是我想出来的代码。在大多数浏览器中似乎可以正常工作,但我想确保它能够在任何地方、任何时间都能正常运行:

var timeoutTime = 1800000;
var timeoutTimer = setTimeout(ShowTimeOutWarning, timeoutTime);
$(document).ready(function() {
    $('body').bind('mousedown keydown', function(event) {
        clearTimeout(timeoutTimer);
        timeoutTimer = setTimeout(ShowTimeOutWarning, timeoutTime);
    });
});

有人发现问题了吗?


4
有一个小问题 - 在开始另一个计时器之前,您需要清除现有的计时器!我已经编辑了这个更改...... - Shog9
谢谢你,Shog9。我经常想到这个问题。但是,用新的计时器覆盖变量不会清除现有的计时器吗? - Darryl Hein
5
不,这个变量只是一个计时器ID,计时器本身是浏览器内部的结构。将ID存储在变量中只是一种简单的方式,用于将内部浏览器结构识别给clearTimeout函数。 - Breton
3
只需要添加一点内容,即在启用触摸的设备上更好地工作,我添加了“touchstart”事件。也就是说,$('body').bind('mousedown keydown touchstart', function(event) {.... - Glade Mellor
5
补充Glade的评论,我也使用鼠标移动,例如:$('body').bind('mousemove mousedown keydown touchstart', function(event) {.... - Parapluie

22

Ifvisible.js是一款跨浏览器的轻量级解决方案,可以检测用户什么时候切换到另一个标签页并回到当前标签页。它还可以检测用户何时空闲和再次活动。它非常灵活。


1
最适合我的答案 (: - Flezcano
最适合我的解决方案。 - Macumbaomuerte

19

你可以观察鼠标移动,但这是你能获取到的用户仍然存在的最好指示,除非监听点击事件。但是JavaScript无法判断它是否为活动选项卡,或者浏览器是否打开。(好吧,您可以获取浏览器的宽度和高度,这样就可以知道它是否被最小化了)


你也可以查看keyup事件。 - TStamper
3
你的意思是没有 keyup 事件吗?当用户停止输入时,最后一个 keyup 事件就发生了,通过设置 keyup 函数事件,你可以检查最近的 keyup 事件发生的时间。 - TStamper
3
我理解您的意思是需要检查两种事件,而不仅仅是其中的一种。这就是我说“还要查看keyup事件”的原因。 - TStamper
3
@TStamper:同意。想象一下,你花了半个小时打了一封长信放在“投诉”框里,就在你想要给最后一个段落加上华丽的结尾侮辱时,网站突然让你退出登录。这将是非常令人恼火的… - Shog9
有一种方法可以检测当前选项卡是否处于活动状态:https://dev59.com/d3NA5IYBdhLWcg3wL6sc - Seppo Erviälä
显示剩余4条评论

16

我最近刚刚做了类似的事情,虽然使用的是Prototype而不是JQuery,但我想只要JQuery支持自定义事件,实现方式应该大致相同。

简而言之,IdleMonitor是一个观察鼠标和键盘事件的类(根据您的需求进行调整)。每30秒它会重置计时器并广播一个state:idle事件,除非它接收到鼠标/键盘事件,此时它会广播一个state:active事件。

var IdleMonitor = Class.create({

    debug: false,
    idleInterval: 30000, // idle interval, in milliseconds
    active: null,
    initialize: function() {
        document.observe("mousemove", this.sendActiveSignal.bind(this));
        document.observe("keypress", this.sendActiveSignal.bind(this));
        this.timer = setTimeout(this.sendIdleSignal.bind(this), this.idleInterval);
    },

    // use this to override the default idleInterval
    useInterval: function(ii) {
        this.idleInterval = ii;
        clearTimeout(this.timer);
        this.timer = setTimeout(this.sendIdleSignal.bind(this), ii);
    },

    sendIdleSignal: function(args) {
        // console.log("state:idle");
        document.fire('state:idle');
        this.active = false;
        clearTimeout(this.timer);
    },

    sendActiveSignal: function() {
        if(!this.active){
            // console.log("state:active");
            document.fire('state:active');
            this.active = true;
            this.timer = setTimeout(this.sendIdleSignal.bind(this), this.idleInterval);
        }
    }
});
然后我只是创建了另一个类,在其中的某个地方有以下内容:
Event.observe(document, 'state:idle', your-on-idle-functionality);
Event.observe(document, 'state:active', your-on-active-functionality)

10

Ifvisible 是一个很好的 JavaScript 库,用于检查用户是否处于非活动状态。

ifvisible.setIdleDuration(120); // Page will become idle after 120 seconds

ifvisible.on("idle", function(){
   // do something
});

3

这是我提供的翻译:

var lastActivityDateTime = null;

function checkActivity( )
{
    var currentTime = new Date();
    var diff = (lastActivityDateTime.getTime( ) - currentTime.getTime( ));
    if ( diff >= 30*60*1000)
    {
        //user wasn't active;
        ...
    }
    setTimeout( 30*60*1000-diff, checkActivity);
}

setTimeout( 30*60*1000, checkActivity); // for first time we setup for 30 min.


// for each event define handler and inside update global timer
$( "body").live( "event_you_want_to_track", handler);

function handler()
{
   lastActivityDateTime = new Date();
   // rest of your code if needed.
}

两个问题:(1)有没有什么理由不断重置计时器?(2)为什么使用live?body标签从未被删除或添加,难道我漏掉了什么吗? - Darryl Hein
  1. 我正在重置计时器,或者我没有理解你的问题。
  2. 使用了live,只是因为在我的代码中5秒钟之前使用过。
- Artem Barger
@Artem- live() 用于查找新动态创建的元素。 - TStamper
你是如何重置计时器的?setTimeout每次事件发生时都会创建另一个超时。 - Darryl Hein
@Darryl:checkActivity 只是定期运行。检查是否已经过了 30 分钟的实际工作是通过将当前时间与 lastActivityDateTime 进行比较来完成的。 - Breton

3
使用jQuery,您可以轻松地监视鼠标移动,并使用它设置一个变量来指示活动状态为true。然后,使用原生JavaScript,您可以每30分钟(或任何其他间隔)检查此变量是否为true。如果为false,则运行您的函数或其他操作。 查找setTimeout和setInterval以进行定时操作。您还可能需要每分钟运行一次函数来将变量重置为false。

2
如果这是一项安全问题,那么使用客户端的Javascript进行此检查绝对是错误的做法。用户很容易禁用javascript:这时你的应用程序该怎么办呢?如果用户在超时之前关闭了浏览器,他们是否会被注销?
大多数服务器端框架都有一些关于登录的会话超时设置。只需使用它并节省工程工作。
你不能依赖于这样的假设:人们无法在没有javascript的情况下登录,因此用户拥有javascript。这种假设对于任何有决心或者甚至是温和教育过的攻击者来说都不是威慑力量。
这样使用javascript就像保安将钥匙交给顾客进入银行金库一样。唯一可行的方法是信任。
请相信我所说的这种使用方式(并且要求登录时使用javascript!!)是一种非常笨拙的设计任何网页应用程序的方法。

1
顺便说一句,如果应用程序使用某种定时的ajax请求来保持信息的实时更新,这可能会更新服务器上的“会话超时”。因此,跟踪您的点击事件并将此信息与您的ajax请求参数一起发送将使“检测”空闲状态更容易。 - gnarf
1
是的,这是一个公正的评论。我的主要观点是门卫应该在服务器上而不是客户端。来自客户端的补充信息可能很有用,但重要的是要为失败做好计划,并且不要依赖客户端代码来保障安全。 - Breton

1

如果不使用JS,更简单(也更安全)的方法是在用户会话中存储lastActivity时间戳,并在页面加载时检查它。假设您正在使用PHP(您可以轻松地为其他平台重新编写此代码):

if(($_SESSION['lastAct'] + 1800) < time()) {
    unset($_SESSION);
    session_destroy();
    header('Location: session_timeout_message.php');
    exit;
}

$_SESSION['lastAct'] = time();

并将此添加到您的页面中(可选,无论页面是否刷新,用户都将注销(因为他在下一页日志上注销))。

<meta http-equiv="refresh" content="1801;" /> 

是的,我已经有了这个。请记住,这只是一个警告,以帮助用户避免丢失信息。这不是网站的身份验证过程!! - Darryl Hein
在这种情况下,只需计算自上次请求以来的时间(如果您没有使用Ajax,则很简单。如果有,则需要每次进行Ajax请求时更新变量)。如果在过去的20分钟内没有发出请求,请显示一个警告,说明他们的会话将在10分钟后过期。 - Andrew Moore
自动刷新页面并不是一个好的做法,因为用户可能正在编辑某些内容,这样会导致他们失去所做的更改。您可以更好地发送 AJAX 请求来定期检查会话。 - Amr

1
您可以根据用户的活动状态向文档添加或删除类。

// If the window is focused, a mouse wheel or touchmove event is detected
$(window).on('focus wheel touchmove', function() {
  $( 'html' ).addClass('active').removeClass('inactive');
});

// If the window losses focus
$(window).on('blur', function() {
  $( 'html' ).addClass('inactive').removeClass('active');
});

之后,您可以定期检查html是否具有“active”类,并发送AJAX请求以检查会话状态并执行所需操作:

setInterval( function() {
  if ( $( 'html' ).hasClass('active') ) {
  //Send ajax request to check the session
    $.ajax({
      //your parameters here
    });
  }
}, 60000 ); /* loops every minute */


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接