javascript - 使用 hashbang 防止用户在单页应用程序中离开路线

标签 javascript sammy.js hashbang

我正在为我的单页应用程序使用 Sammy.js。我想创建类似于 SO 的功能(当您输入问题并尝试离开页面时,它会询问您是否确定)。

如果它不是单页应用,我会做类似的事情:

$(window).bind('beforeunload', function(){
  return 'Are you sure you want to leave?';
});

问题在于,在单页应用程序中,用户实际上并没有离开页面,而是更改了他的 document.location.hash(他可以通过关闭页面来离开页面)。有没有办法为 SPA 制作类似的东西,最好是使用 sammy.js?

最佳答案

在我的工作中,我们在单页 Web 应用程序中遇到了类似的问题需要解决。我们有一些页面可能是脏的,如果它们是脏的,我们希望阻止导航离开该页面,直到用户确认可以这样做。由于我们想阻止导航,我们无法监听 onhashchange 事件,该事件在哈希值更改之后触发,而不是之前。因此,我们决定覆盖默认的 LocationProxy 以包含允许我们在位置更改之前有选择地阻止导航的逻辑。

考虑到这一点,这是我们使用的代理:

PreventableLocationProxy = (function () {
    function PreventableLocationProxy(delegateProxy, navigationValidators) {
        /// <summary>This is an implementation of a Sammy Location Proxy that allows cancelling of setting a location based on the validators passed in.</summary>
        /// <param name="delegateProxy" type="Sammy.DefaultLocationProxy">The Location Proxy which we will delegate all method calls to.</param>
        /// <param name="navigationValidators" type="Function" parameterArray="true" mayBeNull="true">One or more validator functions that will be called whenever someone tries to change the location.</param>
        this.delegateProxy = delegateProxy;
        this.navigationValidators = Array.prototype.slice.call(arguments, 1);
    }

    PreventableLocationProxy.prototype.bind = function () {
        this.delegateProxy.bind();
    };

    PreventableLocationProxy.prototype.unbind = function () {
        this.delegateProxy.unbind();
    };

    PreventableLocationProxy.prototype.getLocation = function () {
        return this.delegateProxy.getLocation();
    };

    PreventableLocationProxy.prototype.setLocation = function (new_location) {
        var doNavigation = true;
        _.each(this.navigationValidators, function (navValidator) {
            if (_.isFunction(navValidator)) {
                // I don't just want to plug the result of the validator in, it could be anything!
                var okayToNavigate = navValidator(new_location);
                // A validator explicitly returning false should cancel the setLocation call. All other values will
                // allow navigation still.
                if (okayToNavigate === false) {
                    doNavigation = false;
                }
            }
        });
        if (doNavigation) {
            return this.delegateProxy.setLocation(new_location);
        }
    };

    return PreventableLocationProxy;
}());

这段代码本身非常简单,它是一个带有委托(delegate)代理以及一个或多个验证器函数的 javascript 对象。如果这些验证器中的任何一个显式返回 false,则导航将被阻止并且位置不会改变。否则,允许导航。为了使这项工作正常进行,我们必须覆盖 anchor 标记的默认 onclick 处理以通过 Sammy.Application.setLocation 对其进行路由。不过,一旦完成,这就可以让我们的应用程序干净利落地处理脏页逻辑。

为了更好地衡量,这是我们的脏页验证器:

function preventNavigationIfDirty(new_location) {
    /// <summary>This is an implementation of a Sammy Location Proxy that allows cancelling of setting a location based on the validators passed in.</summary>
    /// <param name="new_location" type="String">The location that will be navigated to.</param>
    var currentPageModels = [];
    var dirtyPageModels = [];
    //-----

    // Get the IDs of the current virtual page(s), if any exist.
    currentPageModels = _.keys(our.namespace.currentPageModels);

    // Iterate through all models on the current page, looking for any that are dirty and haven't had their changes abored.
    _.forEach(currentPageModels, function (currentPage) {
        if (currentPage.isDirty() && currentPage.cancelled === false) {
            dirtyPageModels.push(currentPage);
        }
    });

    // I only want to show a confirmation dialog if we actually have dirty pages that haven't been cancelled.
    if (dirtyPageModels.length > 0) {
        // Show a dialog with the buttons okay and cancel, and listen for the okay button's onclick event.
        our.namespace.confirmDirtyNavigation(true, function () {
            // If the user has said they want to navigate away, then mark all dirty pages with the cancelled
            // property then do the navigating again. No pages will then prevent the navigation, unlike this
            // first run.

            _.each(dirtyPageModels, function (dirtyPage) {
                dirtyPage.cancelled = true;
            });

            our.namespace.sammy.setLocation(new_location);
        });

        // Returns false in order to explicitly cancel the navigation. We don't need to return anything in any
        // other case.
        return false;
    }
}

请记住,如果用户明确更改位置,此解决方案将不起作用,但这不是我们想要支持的用例。希望这能让您更接近自己的解决方案。

关于javascript - 使用 hashbang 防止用户在单页应用程序中离开路线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23949482/

相关文章:

javascript - 在 javascript 或 JQuery 中查找控件

knockout.js - 如何防止表单提交

angularjs - Angular 路由的可选哈希前缀

jQuery Mobile 和 "query parameters"用于 hashbang 导航

ember.js - 使用 Ember.js 的 Hashbang URL

javascript - Knockout JS 级联状态/城市下拉列表(使用 JSON)

javascript - 如何在 MVC 中使用 ajax 使用新从服务器获取的数据重新初始化 dataTables

javascript - 使用 CSS3 或 Javascript 增加输入字段中闪烁的插入符号宽度和高度

javascript - 使用 Sammy.js 忽略部分引用

knockout.js - Sammy.js 与 knockout 应用程序中的谷歌分析