【问题标题】:Detecting whether user stayed after prompting onBeforeUnload提示onBeforeUnload后检测用户是否停留
【发布时间】:2009-05-04 19:11:21
【问题描述】:

在我正在开发的网络应用程序中,我正在捕获 onBeforeUnload 以询问用户他是否真的想要退出。

现在,如果他决定留下来,我想做很多事情。 我想弄清楚的是,他实际上是选择留下来的。

我当然可以声明“x”秒的 SetTimeout,如果触发,则意味着用户仍然存在(因为我们没有被卸载)。问题是用户可以花任何时间来决定是否留下......

我最初希望在显示对话框时不会触发 SetTimeout 调用,因此我可以设置一个短时间的超时,并且只有在用户选择留下时才会触发。但是,显示对话框时确实会触发超时,因此这不起作用。

我尝试的另一个想法是在窗口/文档上捕获 mouseMoves。显示对话框时,mouseMoves 确实不会触发,除了一个真正适用于我的情况的奇怪异常,因此它也不起作用。

谁能想到其他方法来做到这一点?

谢谢!


(如果您很好奇,捕获 mouseMove 不起作用的原因是我的页面中有一个 IFrame,其中包含来自另一个域的站点。如果在卸载页面时,焦点在IFrame,当对话框显示时,当鼠标从 IFrame 内部移动到外部(至少在 Firefox 中)时,我会触发一次 MouseMove 事件。这可能是一个错误,但很可能会发生在我们的情况,所以我不能使用这种方法)。

【问题讨论】:

    标签: javascript onbeforeunload


    【解决方案1】:

    我最终做的是挂钩到我的文档的点击事件,一旦我收到点击,我就认为用户已经留下了。

    这不是一个好的解决方案,在大多数情况下(如果您是 DiggBar),您会遇到很多误报(人们会留下来,而您永远不会知道),但在我们的情况下,这是完全合理的,因为人们与我们的网站进行了大量互动,而不仅仅是框架网站。

    基本上,我的代码就是这样做的……

    function OnBeforeUnload() {
        Event.observe(document, "click", UserStayed);
        if (IConsiderTheIFrameMightBeTryingToPopOut) {
            return "The site is trying to escape. Do you want to stay?";
        }
    }
    
    function UserStayed() {
    Event.stopObserving(document, "click", UserStayed);
        // New we know the user is still with us for sure.
    }
    

    【讨论】:

    • 代码没问题。如果用户单击“离开此页面”弹出按钮,我想运行 logout() 函数。但是如何检测用户是否点击了“离开此页面”?
    • 你不能。此外,如果用户决定离开,那么尝试注销为时已晚,您可能尝试发送到服务器的 HTTP 请求将立即被取消,并且永远不会到达您的服务器。
    【解决方案2】:

    过去几天我一直在网上研究这个问题,答案总是“不”,你无法确定用户是留下还是离开(除非你的应用程序在以下情况下退出工作)用户离开)。我讨厌“不”的答案。我想出了以下实用程序。

    var OnBeforeUnload = (function(){
        var
        FDUM = new Function,
        AFFIRM = function(){ return true; };
    
        var _reg = function(msg,opts){
            opts = opts || {};
            var
                pid = null,
                pre = typeof opts.prefire == 'function' ? opts.prefire : FDUM,
                callback = typeof opts.callback == 'function' ? opts.callback : FDUM,
                condition = typeof opts.condition == 'function' ? opts.condition : AFFIRM;
    
            window.onbeforeunload = function(){
                return condition() ? (pre(),setTimeout(function(){ pid = setTimeout(callback,20); },1),msg) : void 0; 
            }
    
            window.onunload = function(){ clearTimeout(pid); };
    
        }
    
        var _unreg = function(){ window.onbeforeunload = null;  }
    
        return {
            register : _reg,
            unregister : _unreg
        };
    
    })();
    

    所以,像

    这样的调用
    OnBeforeUnload.register('Hello!',{ 
        condition:function_that_returns_true_to_fire_event_false_to_ignore,
        prefire:function_to_call_on_page_exit_attempt, 
        callback:function_to_call_if_user_stays
    });
    

    condition 将在处理程序中调用以查看它是否应该激活。如果是这样,则在向用户显示弹出窗口之前执行预触发(您可能希望暂停视频),并且只有在用户停留时才会发生回调。

    我的逻辑是在事件处理程序的主体中启动一个 setTimeout。在用户按下其中一个按钮之前,setTimeout 不会执行。在他们这样做之后,它会立即开火。本例中的 setTimeout 并没有调用回调,而是一个代理函数,为回调执行另一个超时,延迟为 20ms。如果用户停留在页面上,则执行回调。如果不是,则将另一个处理程序附加到 onunload 清除将调用回调的超时,确保永远不会调用回调。我只有机会在 Firefox、IE 和 Chrome 中检查它,但到目前为止,它在所有三个中都可以正常工作。

    我希望这可以帮助像我一样对这个问题普遍给出的专利“否”感到沮丧的人们。这段代码可能并不完美,但我认为它在正确的轨道上。

    【讨论】:

    • 这仅在我将超时设置为几秒时才对我有用。否则,当用户单击按钮离开页面时,“回调”(function_to_call_if_user_stays)仍然被调用。就我而言,这是因为触发 onbeforeunload() 的请求链接需要几秒钟才能从服务器返回,所以几秒钟内不会调用 onunload()。
    【解决方案3】:

    这是此类问题的热门话题,我知道特定案例(8 年前)无法使用此解决方案,因为它位于 iFrame 中,但任何未来的人可能会得到更多帮助通过下面的答案而不是上面的答案:

    您可以轻松判断他们是否一直使用 body 事件侦听器,我认为 mousemove 和 keydown 结合起来就足够了。

    要判断他们是否离开,您需要一些服务器端的东西。

    总而言之,它看起来像这样(您必须自己填写对服务器的 ajax 调用):

    window.addEventListener("beforeunload", function(event){
        //tell your server they are attempting to leave
        var tryLeaveID = serverLogAttempToLeave();
    
        //an element with giant letters and warning text, because you can no longer set text on the alert box
        var unsavedWarning = document.getElementById("unsavedChangesWarning");
        unsavedWarning.style.display = "block";
    
        var onStay = function() {
            //if they stay, tell your server so
            serverRevokeAttemptToLeave(tryLeaveID);
            unsavedWarning.style.display = "none";
            document.body.removeEventListener("mousemove", onStay);
            document.body.removeEventListener("keydown", onStay);
        }
    
        document.body.addEventListener("mousemove", onStay);
        document.body.addEventListener("keydown", onStay);
    
    
        var dialogText =  "undisplayed text";
        event.returnValue = dialogText;
        return dialogText;
    });
    

    在服务器端,你将不得不使用一些丑陋的东西,一个计时器。做一堆离开的尝试,并根据逗留时的要求或一分钟左右的确认离开后将其删除。我无法想象知道用户是否离开比几分钟更敏感的任何情况,但如果你有,请发表评论,因为我想考虑一下。

    【讨论】:

      【解决方案4】:

      为什么不直接使用 StackOverflow 提供的方法,您会看到一个弹出框让您做出选择:

      您确定要离开此页面吗?

      您已开始撰写或编辑帖子。

      按 OK 继续,或按 Cancel 留在当前页面

      那么他们需要多长时间并不重要。如果他们点击一个按钮,他们就会离开。如果他们点击另一个,他们就会停留。

      【讨论】:

      • 我正在这样做。但我想在他们留下后做一些事情,只有当他们留下时。我基本上是让服务器知道他们确实留下了,这意味着卸载是由试图离开 IFrame 的东西引起的,而不是由他们关闭窗口引起的。这让我可以记录卸载的可能原因,稍后我可以对此进行处理。但我只想在他们留下的情况下这样做,我想确保他们在通知服务器之前留下。
      • 所以在弹出窗口中使用“我想留下”按钮点击触发“东西”的执行。也许我不明白这个问题。
      • 嗯,“我想留下”按钮属于浏览器,不属于我。我不能触摸它或对它做任何事情,这是一个我根本无法与之交互的标准对话框。据我所知,窗口/文档中没有特定事件会在用户决定留下时触发。
      • 好吧,假设它是一个类似 SO 使用的确认()对话框,您可以使用确认()调用的返回值来确定他们单击了哪个按钮。我不是 100% 肯定的,但我相信如果他们点击“确定”,你会得到一个真实的回报,如果他们点击“取消”它会返回 false。所以你可以处理它来采取你的行动。
      • 如果它是一个确认()对话框,那么它不是我正在使用的。基本上,防止页面卸载的唯一方法是在 onBeforeUnload 事件中返回一些字符串,并且该字符串将在自定义对话框中显示给用户。我想我可以显示一个确认()对话框,但无论他们回答什么都不会阻止页面被卸载...
      猜你喜欢
      • 2011-08-27
      • 2019-08-12
      • 2010-10-30
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多