La mia soluzione per questo è stata una cosa meno automatica di quanto volessi, ma almeno è coerente.
Questo era il mio codice per il salvataggio e il ripristino. Questo codice è stato praticamente trasferito dal mio tentativo alla mia soluzione attuale, l'ho chiamato solo su diversi eventi. "soft" è un flag che proviene da un'azione browser (back, forward o hash click) anziché una chiamata "hard" a Router.navigate(). Durante una chiamata di navigate() volevo solo scorrere verso l'alto.
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;
}
ho anche modificato Backbone.History in modo che possiamo capire la differenza tra reagire a un cambiamento storico "soft" (che chiama checkUrl) rispetto programmazione innescando un "duro" il cambiamento della storia. Passa questo flag al callback del router.
_.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;
},
});
Originariamente stavo cercando di eseguire il salvataggio e il ripristino interamente durante il gestore di hashchange. Più specificamente, all'interno del callback wrapper del router, la funzione anonima che richiama il gestore del percorso effettivo.
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;
},
ho voluto gestire le cose in questo modo, perché permette di salvare in tutti i casi, se un click href, indietro, pulsante avanti, o chiamare il numero navigare().
Il browser ha una "funzione" che tenta di ricordare lo scroll su un hashchange e spostarsi su di esso quando si torna a un hash. Normalmente questo sarebbe stato grandioso e mi avrebbe risparmiato tutta la fatica di implementarlo da solo. Il problema è che la mia app, come molti, modifica l'altezza del DOM da una pagina all'altra.
Ad esempio, sono su una vista #list alta e ho fatto scorrere verso il basso, quindi fare clic su un elemento e andare a una breve visualizzazione #detail che non ha alcuna barra di scorrimento. Quando premo il pulsante Indietro, il browser proverà a scorrere fino all'ultima posizione che ero per la visualizzazione #list. Ma il documento non è ancora così alto, quindi non è in grado di farlo. Nel momento in cui il mio percorso per #elenco viene chiamato e riappare l'elenco, la posizione di scorrimento viene persa.
Quindi, non è possibile utilizzare la memoria di scorrimento incorporata del browser. A meno che non abbia reso il documento un'altezza fissa o abbia fatto alcuni trucchi DOM, cosa che non volevo fare.
Inoltre, il comportamento di scorrimento incorporato compromette il tentativo precedente, poiché la chiamata a saveScrollPosition viene eseguita troppo tardi: il browser ha già modificato la posizione di scorrimento per quel momento.
La soluzione a questo, che avrebbe dovuto essere ovvia, era chiamare saveScrollPosition da Router.navigate() al posto del callback wrapper di route. Questo garantisce che sto salvando la posizione di scorrimento prima che il browser esegua qualcosa su 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);
},
Purtroppo significa anche devo sempre chiamare in modo esplicito navigare() se sono interessato a salvare posizione di scorrimento, piuttosto che semplicemente utilizzando href = "# myhash" nei miei modelli.
Oh bene. Funziona. :-)
Non è un caso che si usi 'position(). Top' e si abbiano elementi genitore che ottengono' position: anything' dopo il caricamento DOM sei tu? – Ohgodwhy
no, stavo usando $ (document) .scrollTop() sia per leggere che per scrivere la posizione di scorrimento – schematic