防止在Backbone驱动的SPA内部页面导航

时间:2015-03-04 12:52:21

标签: backbone.js browser-history pushstate backbone-routing

理由

在我的BB应用程序中,我允许来自排队等待的用户的快速输入在后台定期发送到服务器。我目前遇到的问题是,如果用户离开页面,他们会有效地丢弃队列中的任何待处理更改。

基本上我想要做的就是在用户离开之前告知用户让他们有机会等待保存更改,而不仅仅是退出&丢弃。

细节

因此,对于用户刷新或尝试导航到外部 URL的一般情况,我们可以处理onbeforeunload事件。如果我们处于SPA的上下文中,在页面之间切换不会导致页面刷新,那么它变得有点棘手。

我的直接想法是为所有锚点使用全局点击事件处理程序,并验证是否允许点击,这可用于现场链接导航。但是,这会导致浏览器后退/前进按钮导航。

我还看了Backbone.routefilter,乍一看似乎完全我需要的东西。但是,使用docs中描述的简单案例,路线仍在执行中。

问题

我们如何拦截Backbone SPA中所有场景的导航?

1 个答案:

答案 0 :(得分:1)

直接链接导航

使用全局事件处理程序捕获所有点击事件

$(document).on('click', 'a[href^="/"]', function (e) {
    var href = $(e.currentTarget).attr('href');
    e.preventDefault();
    if (doSomeValidation()) {
        router.navigate(href, { trigger: true });
    }
});

页面刷新/外部网址导航

处理onbeforeunload

上的window事件
$(window).on('beforeunload', function (e) {
    if (!doSomeValidation()) {
        return 'Leaving now will may result in data loss';
    }
});

浏览器后退/前进按钮导航

在幕后Backbone.Router使用最终利用Backbone.historyHTML5 pushstate API。根据您传递给Backbone.history.start的选项以及浏览器的功能,API会挂钩onhashchange事件或onpopstate事件。

深入研究Backbone.history.start的来源很明显,无论您是否使用推送状态,都使用相同的事件处理程序,即checkUrl

if (this._hasPushState) {
    addEventListener('popstate', this.checkUrl, false);
} else if (this._wantsHashChange && this._hasHashChange && !this.iframe) {
    addEventListener('hashchange', this.checkUrl, false);
} else if (this._wantsHashChange) {
    this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
}

因此,我们可以覆盖此方法&在那里执行我们的验证

var originalCheckUrl = Backbone.history.checkUrl;
Backbone.history.checkUrl = function (e) {
    if (doSomeValidation()) {
        return originalCheckUrl.call(this, e);
    } else {
        // re-push the current page into the history (at this stage it's been popped)
        window.history.pushState({}, document.title, Backbone.history.fragment);
        // cancel the original event
        return false;
    }
};