【问题标题】:Best way to detect when a user leaves a web page?检测用户何时离开网页的最佳方法?
【发布时间】:2010-09-13 22:35:51
【问题描述】:

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

onunload JavaScript 事件并非每次都起作用(HTTP 请求花费的时间比终止浏览器所需的时间长)。

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

【问题讨论】:

    标签: javascript


    【解决方案1】:

    尝试onbeforeunload 事件:它在页面卸载之前触发。它还允许您询问用户是否真的想离开。查看演示onbeforeunload Demo

    或者,您可以在他离开时发送Ajax 请求。

    【讨论】:

    • 如果您跟踪表单是否更改并且仅在发生更改时提示,这对用户来说会很好 - 所以这并不烦人。
    • 还要注意,各种移动浏览器会忽略事件的结果(也就是说,它们不会要求用户确认)。 Firefox 在 about:config 中有一个隐藏的偏好来做同样的事情。从本质上讲,这意味着用户始终确认文档可以被卸载。
    • 请注意,当用户刷新页面或提交表单时也会调用此方法。
    • @Andreas Petersson,我有同样的挑战,目标是放弃用户会话。我如何捕获输入的 url 以检查它是否属于项目?
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      这是另一种解决方案 - 因为在大多数浏览器中,导航控件(导航栏、选项卡等)位于页面内容区域的上方,您可以通过以下方式检测离开页面的鼠标指针顶部并显示“在你离开之前”对话框。它完全不显眼并且它允许您在用户实际执行离开操作之前与用户进行交互

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

      缺点是用户实际上打算离开当然是猜测,但在绝大多数情况下它是正确的。

      【讨论】:

      • 我看起来像,我想向我的用户展示一些不显眼的东西,在离开我的页面之前,我看起来像是后退按钮上的悬停事件,因为我需要在用户点击返回。我相信最好的方法是真正检测鼠标离开正文文档,并做一些逻辑来确定用户是否与页面交互。
      【解决方案4】:

      感谢Service Workers,只要浏览器支持,就可以纯粹在客户端实现类似于Adam's 的解决方案。只是绕过心跳请求:

      // 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(() =&gt; fetch('/heartbeat'), 5000)
      • @DanDascalescu 正确。 JS 代码向 /heartbeat 发出网络请求,Service Worker 拦截它。
      【解决方案5】:

      如果你需要做一些异步代码(比如向服务器发送一条消息说用户现在没有关注你的页面),事件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
          }  
      })

      【讨论】:

      • 这也适用于移动设备吗?
      • 这应该是 2022 年公认的答案
      【解决方案6】:

      我知道这个问题已经得到解答,但如果您只想在实际浏览器关闭时触发某些东西,而不仅仅是在页面加载发生时,您可以使用以下代码:

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

      在我的示例中,我正在清除本地存储并使用鼠标 y 坐标提醒用户,仅当浏览器关闭时,这将在程序内的所有页面加载中被忽略。

      【讨论】:

      • window.event 对我来说是未定义的
      • Merr Leader 的示例是错误的,window.event 应该仅用作旧版本 IE 的后备,在其他情况下,事件参数(本例中的 e 变量)应该被使用:stackoverflow.com/questions/9813445/…
      • 不会说我错了。我的示例在 IE 和 Chrome 中适用于我。我的示例是针对用户单击 Web 浏览器上的 X(关闭)的场景。也不是最漂亮的方式,但它确实有效。
      • 来自 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
      【解决方案7】:

      一种(有点笨拙)的方法是使用 AJAX 调用到服务器端替换和链接离开您的站点,指示用户正在离开,然后使用相同的 javascript 块将用户带到他们请求的外部网站。

      当然,如果用户只是关闭浏览器窗口或输入新的 URL,这将不起作用。

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

      【讨论】:

      • 如果您使用报表首页的方式,则无需修改页面中的每个链接。
      【解决方案8】:

      您可以做的是,在页面加载时打开WebSocket 连接,可选择通过标识当前用户的 WebSocket 发送数据,并检查该连接何时在服务器上关闭。

      【讨论】:

        【解决方案9】:

        对于它的价值,这是我所做的,即使文章很旧,它也可能对其他人有所帮助。

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

        对 activeUser.php 的 Ajax 调用

        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 个用户。

        可能不是最有效的解决方案,但确实可以。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-01-10
          • 1970-01-01
          • 1970-01-01
          • 2018-04-06
          • 2011-07-15
          • 1970-01-01
          相关资源
          最近更新 更多