检测用户何时离开网页的最佳方法?


195

检测用户是否离开网页的最佳方法是什么?

onunloadJavaScript事件不起作用每次(HTTP请求花费的时间比需要的时间终止浏览器)。

创建一个可能会被当前的浏览器阻止。

Answers:


219

尝试以下onbeforeunload事件:在页面卸载之前将其触发。它还允许您询问用户是否真的要离开。参见演示onbeforeunload演示

另外,您可以在他离开时发送Ajax请求。


3
如果您跟踪表单是否更改,并且仅在表单更改时提示,这对用户来说是一件好事,因此这并不令人讨厌。
adam0101

3
还要注意,各种移动浏览器都忽略事件的结果(即,它们不要求用户确认)。Firefox在about:config中具有隐藏的首选项以执行相同的操作。从本质上讲,这意味着用户始终会确认文档可能已卸下。
MJB

2
请注意,当用户刷新页面或提交表单时,也会调用此方法。
Zaytsev Dmitry '18

@Andreas Petersson,我对于降低用户会话的目标也遇到了同样的挑战。我如何捕获键入的URL以检查是否属于项目?
Jcc.Sanabria

21

Mozilla开发人员网络很好地描述了onbeforeunload并提供了示例。

如果您想在离开页面之前警告用户,如果页面脏了(即,如果用户输入了一些数据):

window.addEventListener('beforeunload', function(e) {
  var myPageIsDirty = ...; //you implement this logic...
  if(myPageIsDirty) {
    //following two lines will cause the browser to ask the user if they
    //want to leave. The text of this dialog is controlled by the browser.
    e.preventDefault(); //per the standard
    e.returnValue = ''; //required for Chrome
  }
  //else: user is allowed to leave without a warning dialog
});

10

这是另一种解决方案-由于在大多数浏览器中,导航控件(导航栏,标签等)都位于页面内容区域的上方,因此您可以检测到鼠标指针从顶部离开页面并在显示“ 离开之前 ”对话。它完全不引人注目它允许您在用户实际执行要离开的操作之前与其进行交互。

$(document).bind("mouseleave", function(e) {
    if (e.pageY - $(window).scrollTop() <= 1) {    
        $('#BeforeYouLeaveDiv').show();
    }
});

不利的一面是,当然是用户实际上打算离开的猜测,但是在大多数情况下,这是正确的。


我正在寻找类似的东西,我想向用户显示一些不引人注目的东西,在离开页面之前,我正在寻找诸如返回按钮上的悬停事件之类的东西,因为我需要在用户单击返回之前触发该事件。我认为最好的方法实际上是检测鼠标离开正文文档,并执行一些逻辑以确定用户是否与页面进行了交互。
莱昂纳多·索扎·帕瓦


5

我知道已经回答了这个问题,但是如果您只希望在关闭实际的BROWSER时(而不只是在发生页面加载时)触发某些内容,则可以使用以下代码:

window.onbeforeunload = function (e) {
        if ((window.event.clientY < 0)) {
            //window.localStorage.clear();
            //alert("Y coords: " + window.event.clientY)
        }
};

在我的示例中,我正在清除本地存储并用鼠标y坐标警告用户,仅当关闭浏览器时,在程序中所有页面加载时都将忽略此操作。


window.event对我来说是不确定的
Karl Glaser 2014年

Merr Leader的示例是错误的,例如对于旧版本的IE,仅应将window.event用作备用,在其他情况下,应使用事件参数(e在这种情况下为变量): stackoverflow.com/questions/9813445/ …
Sk8erPeter

不会说我错了。我的示例适用于IE和Chrome浏览器。我的示例是针对用户单击Web浏览器上的X(关闭)的情况。也许也不是最漂亮的方法,但是它确实起作用。
Merr领导人

来自MDN:This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. developer.mozilla.org/en-US/docs/Web/API/Window/event
Robin Neal,

3

一种实现方式(略为骇客)是替换和链接,这些链接通过AJAX调用从服务器端离开您的站点,指向服务器端,表明用户正在离开,然后使用相同的JavaScript块将用户带到他们所访问的外部站点已经要求。

当然,如果用户只是关闭浏览器窗口或键入新的URL,则此操作将无效。

为了解决这个问题,您可能需要在页面上使用Javascript的setTimeout(),每隔几秒钟进行一次AJAX调用(取决于您想知道用户是否离开的速度)。


4
如果您使用报表主目录方法,则无需修改页面中的每个链接。
Vinko Vrsalovic

3

多亏了Service Workers,只要浏览器支持,就可以在客户端完全实现类似于Adam的解决方案。只是规避心跳请求:

// The delay should be longer than the heartbeat by a significant enough amount that there won't be false positives
const liveTimeoutDelay = 10000
let liveTimeout = null

global.self.addEventListener('fetch', event => {
  clearTimeout(liveTimeout)
  liveTimeout = setTimeout(() => {
    console.log('User left page')
    // handle page leave
  }, liveTimeoutDelay)
  // Forward any events except for hearbeat events
  if (event.request.url.endsWith('/heartbeat')) {
    event.respondWith(
      new global.Response('Still here')
    )
  }
})

您是说在客户端代码的某处需要一个setInterval(() => fetch('/heartbeat'), 5000)吗?
Dan Dascalescu

@DanDascalescu正确。JS代码向/ heartbeat发出网络请求,服务工作者将其拦截。
杰弗里·斯威尼

2

在您需要执行一些异步代码的情况下(例如向服务器发送一条消息,表明用户当前不在您的页面上),该事件beforeunload不会给异步代码运行时间。在异步情况下,我发现visibilitychangemouseleave事件是最好的选择。当用户更改选项卡,隐藏浏览器或将课程表移出窗口范围时,将触发这些事件。

document.addEventListener('mouseleave', e=>{
     //do some async code
})

document.addEventListener('visibilitychange', e=>{
     if (document.visibilityState === 'visible') {
   //report that user is in focus
    } else {
     //report that user is out of focus
    }  
})


0

对于它的价值,这就是我所做的,即使这篇文章比较陈旧,它也可以帮助其他人。

PHP:

session_start();

$_SESSION['ipaddress'] = $_SERVER['REMOTE_ADDR'];

if(isset($_SESSION['userID'])){
    if(!strpos($_SESSION['activeID'], '-')){
        $_SESSION['activeID'] = $_SESSION['userID'].'-'.$_SESSION['activeID'];
    }
}elseif(!isset($_SESSION['activeID'])){
    $_SESSION['activeID'] = time();
}

JS

window.setInterval(function(){
            var userid = '<?php echo $_SESSION['activeID']; ?>';
            var ipaddress = '<?php echo $_SESSION['ipaddress']; ?>';
            var action = 'data';

            $.ajax({
                url:'activeUser.php',
                method:'POST',
                data:{action:action,userid:userid,ipaddress:ipaddress},
                success:function(response){
                     //alert(response);                 
                }
            });
          }, 5000);

Ajax调用activeUser.php

if(isset($_POST['action'])){
    if(isset($_POST['userid'])){
        $stamp = time();
        $activeid = $_POST['userid'];
        $ip = $_POST['ipaddress'];

        $query = "SELECT stamp FROM activeusers WHERE activeid = '".$activeid."' LIMIT 1";
        $results = RUNSIMPLEDB($query);

        if($results->num_rows > 0){
            $query = "UPDATE activeusers SET stamp = '$stamp' WHERE activeid = '".$activeid."' AND ip = '$ip' LIMIT 1";
            RUNSIMPLEDB($query);
        }else{
            $query = "INSERT INTO activeusers (activeid,stamp,ip)
                    VALUES ('".$activeid."','$stamp','$ip')";
            RUNSIMPLEDB($query);
        }
    }
}

数据库:

CREATE TABLE `activeusers` (
  `id` int(11) NOT NULL,
  `activeid` varchar(20) NOT NULL,
  `stamp` int(11) NOT NULL,
  `ip` text
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

基本上每5秒钟js就会发布到一个php文件,该文件将跟踪用户和用户的IP地址。活动用户只是数据库记录,该记录在5秒钟内更新了数据库时间戳。老用户停止更新到数据库。ip地址仅用于确保用户唯一,因此该站点上的2个用户不能同时注册为1个用户。

可能不是最有效的解决方案,但可以完成工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.