sch*_*tic 27 javascript jquery backbone.js
我已阅读了许多相关的主题,但似乎没有一个提供解决方案.
我正在尝试做的是在我的Backbone.js应用程序中智能地处理滚动条.像许多其他人一样,我有多个#mypage哈希路由.其中一些路线是分层的.例如,我有一个列出一些项目的#list页面,我点击列表中的项目.然后它打开一个#view/ITEMID页面.
我的页面都在HTML布局中共享相同的Content div.在导航更改中,我将一个新的div注入到Content div中,代替该路径的视图,替换之前的内容.
所以现在我的问题:
如果项目在列表中很远,我可能需要滚动才能到达那里.当我点击它时,"默认"Backbone行为是#view/ITEMID页面显示在#list视图所在的相同滚动位置.修复很容易; 只要注入新视图,只需添加$(文档).scrollTop(0).
问题是,如果我按下后退按钮,我想回到之前滚动位置的#list视图.
我试图采取明显的解决方案.存储路线图以滚动内存中的位置.我在hashchange事件的处理程序的开头写入此映射,但在将新视图实际放入DOM之前.在新视图位于DOM之后,我从hashchange处理程序末尾的地图中读取.
我注意到的是,至少在Firefox中的某个地方,滚动页面作为hashchange事件的一部分,因此当我的写入地图代码被调用时,文档的滚动位置不稳定.绝对没有用户明确提出.
任何人都知道如何解决这个问题,或者我应该使用的最佳做法?
我仔细检查过,我的DOM中没有与我正在使用的哈希相匹配的锚标签.
sch*_*tic 14
我的解决方案最终变得不那么自动,但至少它是一致的.
这是我保存和恢复的代码.这个代码几乎是从我对实际解决方案的尝试中获得的,只是在不同的事件上调用它."soft"是一个标志,它来自浏览器操作(后退,前进或哈希点击),而不是对Router.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;
}
Run Code Online (Sandbox Code Playgroud)
我还修改了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;
},
});
Run Code Online (Sandbox Code Playgroud)
最初我试图在hashchange处理程序中完成滚动保存和恢复.更具体地说,在Router的回调包装器中,调用实际路由处理程序的匿名函数.
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;
},
Run Code Online (Sandbox Code Playgroud)
我想以这种方式处理事情,因为它允许在所有情况下保存,无论是href单击,后退按钮,前进按钮还是导航()调用.
浏览器有一个"功能",它试图记住您在哈希变换上的滚动,并在返回哈希时移动到它.通常情况下这会很棒,并且可以省去我自己实施它的所有麻烦.问题是我的应用程序和许多人一样,在页面之间改变了DOM的高度.
例如,我在一个很高的#list视图中滚动到底部,然后单击一个项目并转到一个没有滚动条的简短#detail视图.当我按下后退按钮时,浏览器会尝试将我滚动到我为#list视图的最后位置.但是文件还不高,所以它无法做到.当我的#list路由被调用并且我重新显示列表时,滚动位置将丢失.
因此,无法使用浏览器的内置滚动内存.除非我将文档设置为固定高度或做了一些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);
},
Run Code Online (Sandbox Code Playgroud)
不幸的是,这也意味着如果我对保存滚动位置感兴趣,而不是在我的模板中使用href ="#myhash",我总是必须显式调用navigate().
那好吧.有用.:-)
一个简单的解决方案:将列表视图的位置存储在变量中的每个滚动事件中:
var pos;
$(window).scroll(function() {
pos = window.pageYOffset;
});
Run Code Online (Sandbox Code Playgroud)
从项目视图返回时,将列表视图滚动到存储位置:
window.scrollTo(0, pos);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
13337 次 |
| 最近记录: |