9

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

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

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

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

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

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

谢谢!


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

4

4 回答 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.
}
于 2009-06-18T15:13:29.733 回答
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 中检查它,但到目前为止它在所有三个中都有效,没有出现任何问题。

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

于 2013-03-06T06:17:35.003 回答
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;
});

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

于 2017-07-19T17:59:49.487 回答
-7

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

您确定要离开此页面吗?

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

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

然后,他们需要多长时间并不重要。如果他们点击一个按钮,他们就会离开。如果他们点击另一个,他们会留下来。

于 2009-05-05T17:55:01.070 回答