私の解決策は私の望むものよりも自動化されていませんが、少なくとも一貫しています。
これは保存と復元のためのコードです。このコードは、私の試みから実際のソリューションまで運ばれてきました。ちょうど異なるイベントで呼び出されました。 "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;
}
我々はプログラムで「ハード」履歴変更をトリガー対(checkUrlを呼び出す)「ソフト」履歴変更に反応の違いを伝えることができるように、私もBackbone.Historyを変更しました。このフラグをルータのコールバックに渡します。
_.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;
},
});
元々、私はスクロールの保存をやり直し、ハッシュ変更ハンドラ中に完全に復元しようとしていました。具体的には、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;
},
それはhrefのクリック、戻るボタン、進むボタン、または()の呼び出しをナビゲートするかどうか、全てのケースに保存できますので、私は物事をこのように処理するために望んでいました。
ブラウザには、ハッシュチェンジのスクロールを覚えて、ハッシュに戻るときに移動する「機能」があります。通常これは素晴らしいことでしたし、それを自分で実装するすべての手間を省くことができます。問題は私のアプリは、多くの場合と同様に、DOMの高さをページごとに変更することです。
たとえば、#リストビューが高く、下にスクロールした後、アイテムをクリックして、スクロールバーがまったくない短い#detailビューに移動します。戻るボタンを押すと、ブラウザは#listビューの最後の位置にスクロールしようとします。しかし、文書はそれほど大きくはないので、そうすることはできません。 #listの私のルートが呼び出されてリストを再表示するまでに、スクロール位置は失われます。
したがって、ブラウザの内蔵スクロールメモリを使用できませんでした。ドキュメントを一定の高さにしたり、DOMトリッキーをしたりしない限り、私はやりたくなかった。
さらに、saveScrollPositionの呼び出しが遅すぎるため、組み込みのスクロール動作が上の試みを邪魔します。ブラウザは既にスクロール位置を変更しています。
明白であったはずのこのソリューションは、ルートコールバックラッパーの代わりにRouter.navigate()からsaveScrollPositionを呼び出していました。これは、ブラウザがハッシュ変更を行う前にスクロール位置を保存していることを保証します。
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 =「#1 myhash」を使用するのではなく、私は、スクロール位置を保存することに興味があれば、私はいつも)(明示的にナビゲートをコールする必要があるということです。
まあまあです。できます。 :-)
あなたは偶然 'position()。top'を使用しておらず、親要素がDOMのロード後に' position:anything'を取得していますか? – Ohgodwhy
いいえ、私はスクロール位置を読み書きするために$(document).scrollTop()を使用していました – schematic