我對此的解決方案最終導致自動化程度低於我想要的,但至少它是一致的。
這是我的代碼保存和恢復。這段代碼幾乎是從我的實際解決方案中進行的,只是在不同的事件中調用它。 「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;
}
我也修改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點擊,後退按鈕,前進按鈕,或導航()調用。
瀏覽器有一個「功能」,它試圖記住你在hashchange上的滾動,並在回到散列時移動到它。通常情況下,這會很好,並且可以節省我自己實施它的所有麻煩。問題是我的應用程序與許多應用程序一樣,會改變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);
},
不幸的是,這也意味着我總是有顯式調用瀏覽()如果我想救滾動位置,而不是在我的模板,只是用的href =「#myhash」。
唉。有用。 :-)
你不是偶然的使用'position()。top'並且父元素在DOM負載之後獲得'position:anything'嗎? – Ohgodwhy
不,我正在使用$(document).scrollTop()來讀取和寫入滾動位置 – schematic