检测用户在提示 onBeforeUnload 后是否停留

9
在我正在开发的一个Web应用程序中,我正在捕获onBeforeUnload事件,询问用户是否真的要退出。
现在,如果他决定留下,有很多事情我想做。我试图弄清楚的是,他实际上选择留下了。
当然,我可以声明一个“x”秒的SetTimeout,如果它触发了,那就意味着用户仍然在那里(因为我们没有被卸载)。问题是,用户可以花任何时间决定是否留下...
我最初希望在对话框显示时不会触发SetTimeout调用,因此我可以为短时间设置超时,并且只有在用户选择留下时才会触发。但是,超时确实在显示对话框时触发,因此无法工作。
另一个想法是在窗口/文档上捕获mouseMoves。当显示对话框时,的确不会触发mouseMoves,除了一个奇怪的例外情况,这确实适用于我的情况,所以这也行不通。
有人能想出其他方法吗?
谢谢!
(如果您好奇,那么捕获mouseMove不起作用的原因是我在页面中有一个IFrame,其中包含来自另一个域的站点。如果在卸载页面时,焦点在IFrame内,则对话框显示时我会在鼠标从内部移动到外部时触发一次MouseMove事件(至少在Firefox中是这样)。那可能是一个错误,但仍然很可能会发生在我们的情况下,因此我不能使用这种方法。)
4个回答

6
我最终做的是钩入文档的点击事件,一旦我收到了点击事件,我就认为用户已停留。
在大多数情况下这不是一个好的解决方案(如果你是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()函数。但是我如何检测用户是否已经点击了“离开此页”? - Bit_hunter
你不能这样做。而且,如果用户决定离开,尝试注销已经太迟了,你可能正在尝试发送的HTTP请求会立即被取消,永远无法到达你的服务器。 - Daniel Magliola

4

我最近在网上研究这个问题,答案总是“不行”,你无法确定用户是留下还是离开(除非用户离开,你的应用程序就停止工作了)。我讨厌“不行”的答案。我想出了以下实用工具。

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

处理程序内将调用该条件来确定是否应该激活。如果是这样,那么在弹出窗口显示给用户之前,将执行预先操作(例如暂停视频),只有用户留在网页上时才会发生回调。

我的逻辑是在事件处理程序的主体中启动setTimeout。直到用户按下按钮后,setTimeout才会执行。在他们按下按钮后,它立即触发。在本例中,setTimeout不会调用回调函数,而是代理一个函数,该函数使用20ms的延迟执行另一个回调的超时。如果用户留在页面上,则执行回调。如果没有,那么将附加另一个处理程序到onunload上,该处理程序清除将调用回调的超时,确保永远不调用回调。我只有在Firefox、IE和Chrome中检查过它,但迄今为止在所有三个浏览器中都没有问题。

我希望这可以帮助像我一样受到广泛拒绝的人们。这段代码可能不完美,但我认为它走在正确的轨道上。


只有当我将超时设置为几秒钟时,这才对我有效。否则,当用户单击按钮离开页面时,“回调”(function_to_call_if_user_stays)仍会被调用。在我的情况下,这是因为触发onbeforeunload()的请求链接需要几秒钟才能从服务器返回,因此onunload()要等待几秒钟才会被调用。 - CyberMonk

0

这是此类问题的热门解决方案,我知道具体情况(来自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;
});

在服务器端,你需要使用一些不太好看的东西,比如计时器。创建一个尝试离开的堆栈,在停留请求后或经过一分钟确认离开后将其删除。我无法想象任何情况下知道用户是否离开比几分钟更加时间敏感,但如果你有,请评论,因为我想考虑一下。

-7

为什么不直接使用 StackOverflow 所使用的方法,即弹出一个弹窗框,让您进行选择:

您确定要离开此页面吗?

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

按“确定”继续,或按“取消”留在当前页面上

然后不管他们花多长时间都没关系。如果他们点击一个按钮,他们就会离开。如果他们点击另一个按钮,他们就会留下来。


我正在做这个。但是只有在他们停留的情况下,我想要执行一些操作。基本上,我让服务器知道他们确实停留了,这意味着卸载是由于某个尝试离开IFrame的东西引起的,而不是他们关闭窗口引起的。这样我就可以记录卸载的可能原因,然后再进行相关操作。但是我只想在他们停留的情况下执行这些操作,我想要确保他们在通知服务器之前已经停留下来。 - Daniel Magliola
请使用弹出窗口中的“我想留下”按钮单击来触发“stuff”的执行。也许我没有理解问题。 - Chad Birch
1
嗯,"我想留下"按钮属于浏览器,而不是我。我无法触摸它或对其进行任何操作,它是一个标准对话框,我无法与之交互。 就我所知,在窗口/文档中没有特定的事件会在用户决定留下时触发。 - Daniel Magliola
好的,假设它是一个类似 SO 使用的 confirm() 对话框,你可以使用 confirm() 调用的返回值来确定用户点击了哪个按钮。我不是百分之百确定,但我相信如果他们点击了“确定”,你会得到一个 true 的返回值,如果他们点击了“取消”,则会返回 false。因此,你可以处理这个返回值来执行你的操作。 - Chad Birch
如果是confirm()对话框,那不是我正在使用的。基本上,防止页面卸载的唯一方法是在onBeforeUnload事件中返回一些字符串,并将该字符串显示给用户在自定义对话框中。我猜我可以显示一个confirm()对话框,但无论他们在那里回答什么都不能阻止页面被卸载... - Daniel Magliola
嗯,也许你可以深入研究一下SO的JavaScript,看看他们如何使用那个对话框。在我已经开始编写内容但仍试图离开/关闭页面时,它似乎一直都很有效。此外,他们显然有一些方法来阻止你在点击取消后离开页面。 - Chad Birch

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