Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/380.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jquery/73.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何在Backbone.js应用程序中处理hashchange上的滚动位置?_Javascript_Jquery_Backbone.js - Fatal编程技术网

Javascript 如何在Backbone.js应用程序中处理hashchange上的滚动位置?

Javascript 如何在Backbone.js应用程序中处理hashchange上的滚动位置?,javascript,jquery,backbone.js,Javascript,Jquery,Backbone.js,我读过很多相关的文章,但似乎没有一个能提供解决方案 我要做的是在我的Backbone.js应用程序中智能地处理滚动条。与其他许多人一样,我有多条#mypage哈希路由。其中一些路由是分层的。e、 g.我有一个列出一些项目的列表页面,我点击列表中的一个项目。然后打开一个#视图/ITEMID页面 我的页面在HTML布局中共享相同的内容div。在一次导航更改中,我将一个表示该路由视图的新div插入到contentdiv中,替换以前存在的任何内容 现在我的问题是: 如果该项目在列表中的位置较低,我可能需

我读过很多相关的文章,但似乎没有一个能提供解决方案

我要做的是在我的Backbone.js应用程序中智能地处理滚动条。与其他许多人一样,我有多条#mypage哈希路由。其中一些路由是分层的。e、 g.我有一个列出一些项目的列表页面,我点击列表中的一个项目。然后打开一个#视图/ITEMID页面

我的页面在HTML布局中共享相同的内容div。在一次导航更改中,我将一个表示该路由视图的新div插入到contentdiv中,替换以前存在的任何内容

现在我的问题是:

如果该项目在列表中的位置较低,我可能需要滚动才能到达那里。当我单击它时,“默认”主干行为是#视图/ITEMID页面显示在与#列表视图相同的滚动位置。解决这个问题很容易;只要在插入新视图时添加$(document).scrollTop(0)

问题是,如果我按下“后退”按钮,我想返回到以前滚动位置的#列表视图

我试图采取显而易见的解决办法。将路线图存储到内存中的滚动位置。我在hashchange事件的处理程序开始时写入此映射,但在新视图实际放入DOM之前。在新视图位于DOM中之后,我在hashchange处理程序的末尾读取映射

我注意到的是,至少在Firefox中的某个地方,某个地方正在滚动页面,作为hashchange事件的一部分,因此在调用我的write to map代码时,文档有一个不稳定的滚动位置,这肯定不是用户明确做出的

有人知道如何解决这个问题,或者知道我应该使用的最佳实践吗


我仔细检查了一下,我的DOM中没有与我正在使用的哈希匹配的锚标记。

我的解决方案没有我想要的那么自动化,但至少它是一致的

这是我保存和恢复的代码。这段代码基本上是从我的尝试带到我的实际解决方案,只是在不同的事件中调用它。“soft”是一个标志,它来自浏览器操作(后退、前进或散列单击),而不是对Router.navigate()的“硬”调用。在一次navigate()调用中,我只想滚动到顶部

restoreScrollPosition: function(route, soft) {
    var pos = 0;
    if (soft) {
        if (this.routesToScrollPositions[route]) {
            pos = this.routesToScrollPositions[route];
        }
    }
    else {
        delete this.routesToScrollPositions[route];
    }
    $(window).scrollTop(pos);
},

saveScrollPosition: function(route) {
    var pos = $(window).scrollTop();
    this.routesToScrollPositions[route] = pos;
}
我还修改了Backbone.History,这样我们就可以区分响应“软”历史更改(调用checkUrl)和以编程方式触发“硬”历史更改。它将此标志传递给路由器回调

_.extend(Backbone.History.prototype, {

    // react to a back/forward button, or an href click.  a "soft" route
   checkUrl: function(e) {
        var current = this.getFragment();
        if (current == this.fragment && this.iframe)
            current = this.getFragment(this.getHash(this.iframe));
        if (current == this.fragment) return false;
        if (this.iframe) this.navigate(current);
        // CHANGE: tell loadUrl this is a soft route
        this.loadUrl(undefined, true) || this.loadUrl(this.getHash(), true);
    },

    // this is called in the whether a soft route or a hard Router.navigate call
    loadUrl: function(fragmentOverride, soft) {
        var fragment = this.fragment = this.getFragment(fragmentOverride);
        var matched = _.any(this.handlers, function(handler) {
            if (handler.route.test(fragment)) {
                // CHANGE: tell Router if this was a soft route
                handler.callback(fragment, soft);
                return true;
            }
        });
        return matched;
    },
});
最初,我试图在hashchange处理程序中完全执行滚动保存和恢复。更具体地说,在路由器的回调包装器中,调用实际路由处理程序的匿名函数

route: function(route, name, callback) {
    Backbone.history || (Backbone.history = new Backbone.History);
    if (!_.isRegExp(route)) route = this._routeToRegExp(route);
    if (!callback) callback = this[name];
    Backbone.history.route(route, _.bind(function(fragment, soft) {

        // CHANGE: save scroll position of old route prior to invoking callback
        // & changing DOM
        displayManager.saveScrollPosition(foo.lastRoute);

        var args = this._extractParameters(route, fragment);
        callback && callback.apply(this, args);
        this.trigger.apply(this, ['route:' + name].concat(args));

        // CHANGE: restore scroll position of current route after DOM was changed
        // in callback
        displayManager.restoreScrollPosition(fragment, soft);
        foo.lastRoute = fragment;

        Backbone.history.trigger('route', this, name, args);
    }, this));
    return this;
},
我希望以这种方式处理问题,因为它允许在所有情况下进行保存,无论是href click、back按钮、forward按钮还是navigate()调用

浏览器有一个“功能”,它尝试在哈希更改上记住您的滚动,并在返回哈希时移动到滚动。通常情况下,这将是伟大的,并将节省我所有的麻烦,实施它自己。问题是我的应用程序和许多应用程序一样,会在页面之间更改DOM的高度

例如,我在一个高的列表视图上,滚动到底部,然后单击一个项目,转到一个没有滚动条的短的详细视图。当我按下“后退”按钮时,浏览器将尝试将我滚动到我在#列表视图中的最后一个位置。但这份文件还没有那么高,所以它无法做到这一点。当调用我的#列表路由并重新显示列表时,滚动位置已丢失

因此,无法使用浏览器的内置滚动内存。除非我将文档设置为固定高度,或者做了一些我不想做的DOM欺骗

此外,内置的滚动行为将上述尝试弄糟,因为调用saveScrollPosition太晚了——此时浏览器已经更改了滚动位置

解决这个问题的方法是从Router.navigate()调用saveScrollPosition,而不是路由回调包装器,这应该是显而易见的。这保证了我在浏览器对hashchange执行任何操作之前保存滚动位置

route: function(route, name, callback) {
    Backbone.history || (Backbone.history = new Backbone.History);
    if (!_.isRegExp(route)) route = this._routeToRegExp(route);
    if (!callback) callback = this[name];
    Backbone.history.route(route, _.bind(function(fragment, soft) {

        // CHANGE: don't saveScrollPosition at this point, it's too late.

        var args = this._extractParameters(route, fragment);
        callback && callback.apply(this, args);
        this.trigger.apply(this, ['route:' + name].concat(args));

        // CHANGE: restore scroll position of current route after DOM was changed
        // in callback
        displayManager.restoreScrollPosition(fragment, soft);
        foo.lastRoute = fragment;

        Backbone.history.trigger('route', this, name, args);
    }, this));
    return this;
},

navigate: function(route, options) {
    // CHANGE: save scroll position prior to triggering hash change
    nationalcity.displayManager.saveScrollPosition(foo.lastRoute);
    Backbone.Router.prototype.navigate.call(this, route, options);
},
不幸的是,这也意味着如果我想保存滚动位置,就必须显式调用navigate(),而不是在模板中只使用href=“#myhash”


哦,好吧。它有效。:-)

我有一个稍微可怜的办法。在我的应用程序中,我遇到了类似的问题。我通过将列表视图和项目视图放入一个包含以下内容的容器来解决此问题:

height: 100%
然后,我将列表视图和项目视图都设置为:

overflow-y: auto
height: 100%
然后,当我单击一个项目时,我隐藏列表并显示项目视图。这样,当我关闭项目并返回列表时,我会保留我在列表中的位置。它只与“后退”按钮一起工作一次,但显然它不会保留您的历史记录,因此多次单击“后退”按钮将无法让您到达需要的位置。不过,这是一个没有JS的解决方案,所以如果它足够好的话…

一个简单的解决方案: 将列表视图在每个滚动事件中的位置存储在变量中:

var pos;
$(window).scroll(function() {
    pos = window.pageYOffset;
});
从项目视图返回时,将列表视图滚动到存储位置:

window.scrollTo(0, pos);

@幻影114感谢您发布您的解决方案。它就像一个符咒。只是一件小事,它假设路由函数是同步的。如果存在异步操作,例如在呈现视图之前获取远程数据,则在将视图内容添加到DOM之前滚动窗口。在我的例子中,我在第一次访问路由时缓存数据。这样,当用户点击浏览器的“后退/前进”按钮时,就避免了获取数据的异步操作。但是,缓存路由所需的所有数据可能并不总是可能的

你不是偶然使用了