Giải pháp của tôi cho điều này đã trở thành thứ gì đó ít tự động hơn tôi muốn, nhưng ít nhất là nó nhất quán.
Đây là mã của tôi để lưu và khôi phục. Mã này đã được thực hiện khá nhiều từ nỗ lực của tôi đối với giải pháp thực tế của tôi, chỉ cần gọi nó trên các sự kiện khác nhau. "mềm" là một lá cờ mà điều này xuất phát từ một hành động trình duyệt (quay lại, chuyển tiếp hoặc nhấp chuột băm) trái ngược với lời gọi "cứng" tới Router.navigate(). Trong suốt cuộc gọi điều hướng(), tôi muốn cuộn lên trên cùng.
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;
}
Tôi cũng sửa đổi Backbone.History để chúng ta có thể biết sự khác biệt giữa phản ứng với một sự thay đổi lịch sử "mềm" (trong đó kêu gọi checkUrl) so với lập trình gây ra một "cứng" thay đổi lịch sử. Nó chuyển cờ này đến cuộc gọi lại của 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;
},
});
Ban đầu, tôi đã cố gắng lưu và lưu khôi phục hoàn toàn trong trình xử lý hashchange. Cụ thể hơn, trong trình bao bọc gọi lại của Bộ định tuyến, hàm ẩn danh gọi trình xử lý tuyến đường thực tế của bạn.
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;
},
tôi muốn xử lý mọi thứ theo cách này vì nó cho phép tiết kiệm trong mọi trường hợp, cho dù một nhấp chuột href, trở lại nút, nút phía trước, hoặc điều hướng() gọi.
Trình duyệt có "tính năng" cố gắng nhớ cuộn của bạn trên một hashchange và di chuyển đến nó khi quay lại băm. Thông thường điều này sẽ rất tuyệt, và sẽ giúp tôi giải quyết mọi rắc rối khi tự mình thực hiện nó. Vấn đề là ứng dụng của tôi, giống như nhiều ứng dụng, thay đổi chiều cao của DOM từ trang này sang trang khác.
Ví dụ: Tôi đang ở chế độ xem #list cao và đã cuộn xuống dưới cùng, sau đó nhấp vào một mục và chuyển đến chế độ xem #detail ngắn không có thanh cuộn. Khi tôi nhấn nút Quay lại, trình duyệt sẽ cố gắng cuộn tôi đến vị trí cuối cùng mà tôi đã cho chế độ xem #list. Nhưng tài liệu không cao nhưng vẫn không thể làm được. Vào thời điểm tuyến đường của tôi cho #list được gọi và tôi hiển thị lại danh sách, vị trí cuộn bị mất.
Vì vậy, không thể sử dụng bộ nhớ cuộn được tích hợp sẵn của trình duyệt. Trừ khi tôi làm cho tài liệu có chiều cao cố định hoặc đã thực hiện một số thủ thuật DOM, điều mà tôi không muốn làm.
Hơn nữa, hành vi cuộn tích hợp sẽ làm rối loạn nỗ lực trên, vì lệnh gọi đến saveScrollPosition được thực hiện quá muộn - trình duyệt đã thay đổi vị trí cuộn lúc đó.
Giải pháp cho điều này, điều này cần phải rõ ràng, đã gọi saveScrollPosition từ Router.navigate() thay vì trình bao bọc gọi lại tuyến đường. Điều này đảm bảo rằng tôi đang lưu vị trí cuộn trước khi trình duyệt thực hiện bất kỳ điều gì trên 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);
},
Thật không may nó cũng có nghĩa là tôi luôn gọi điều hướng() nếu tôi muốn lưu vị trí cuộn, thay vì chỉ sử dụng href = "# myhash" trong mẫu của tôi.
Ồ tốt. Nó hoạt động. :-)
Bạn không có cơ hội sử dụng 'position(). Top' và có các phần tử cha có được' vị trí: bất cứ thứ gì' sau tải DOM? – Ohgodwhy
không, tôi đã sử dụng $ (tài liệu) .scrollTop() cho cả việc đọc và viết vị trí cuộn – schematic