Search code examples
javascriptjquerybackbone.js

Backbone back button scroll issue


I am new to Backbone and everything was going well before decided to preserve scrollTop position between a long list of items and an item view. I managed to save the scroll position of the list and item view and everything is fine but when pressing the browser back button there is an autoscrolling from the browser going back to the position of the listView before actually going back to the list view. I googled and found nothing about this behavior. Is there any solution to this?

Thank you in advance for any help.

App.prototype.initialize = function() {
  var e, error1, fallbackLanguage, language;
  L.debug("App.initialize -> triggered.");
  try {
    Backbone.noConflict();
    _.noConflict();
    window.app = {};
    window.app.scrollYHistory = {};
    window.app.history = [];
    this.listenTo(Backbone, "translate", _translate);
    this.listenTo(Backbone, "changeLanguage", _changeLanguage);
    this.listenTo(Backbone, "setServerLanguage", _setServerLanguage);
    this.listenTo(Backbone, "userLoggedOut", _destroyJWT);
    this.listenTo(Backbone, "setJWT", _setJWT);
    this.listenTo(Backbone.history, "route", _history);
    _UASupportAnimation();
    _UASupportCookie();
    _UASupportLocalStorage();
    _UASupportTransition();
    _cssExtra();
    window.onerror = function(error) {
      return L.error(error);
    };
    new MainRouter();
    new UserRouter();
    new AdminRouter();
    fallbackLanguage = LANGUAGE_SUPPORTED[LANGUAGE_DEFAULT];
    language = _getLanguage();
    L.debug("App.initialize -> i18n module.");
    return $.i18n.init({
      "useCookie": false,
      "fallbackLng": fallbackLanguage,
      "lng": language,
      "load": "current",
      "ns": {
        "namespaces": ["translation", "validation"],
        "defaultNs": "translation"
      },
      "resGetPath": "/static/languages/__lng__/__ns__.json",
      "useDataAttrOptions": true
    }).done(function() {
      L.debug("App.initialize -> i18n initialization done, starting the application.");
      _setServerLanguage();
      Backbone.history.start({
        "root": "/",
        "pushState": true,
        "hashChange": true
      });
      return L.debug("App.initialize -> initialization completed.");
    });
  } catch (error1) {
    e = error1;
    return L.error(e);
  }
};
_history = function(router, handler, args) {
  var e, error1, lastPage;
  try {
    lastPage = window.app.history[window.app.history.length - 1];
    if (!_.isUndefined(lastPage)) {
      _saveScrollYPosition(lastPage);
    }
    return window.app.history.push(window.location.pathname);
  } catch (error1) {
    e = error1;
    return L.error(e);
  }
};

_saveScrollYPosition = function(pathname) {
  var e, error1;
  try {
    L.debug("App._saveScrollYPosition -> triggered.");
    return window.app.scrollYHistory[pathname || window.location.pathname] = document.body.scrollTop;
  } catch (error1) {
    e = error1;
    return L.error(e);
  }
};



function AdView() {
  return AdView.__super__.constructor.apply(this, arguments);
}

AdView.prototype.tagName = "section";

AdView.prototype.className = "l-page l-page--ad";

AdView.prototype.template = _.template($(Template).html());

AdView.prototype.initialize = function(model) {
  var e, error;
  this.model = model;
  if (!this.model) {
    throw new Error("adView.initialize -> can't initialize without my model!");
  }
  try {
    this.Header = HeaderView;
    this.Footer = FooterView;
    this.adModel = this.model.adModel.toJSON();
    this.scrollYPosition = window.app.scrollYHistory[window.location.pathname] || 0;
    this.compiledTemplate = this.template(this.adModel);
    return L.debug((this.getMyName()) + ".initialize -> done.");
  } catch (error) {
    e = error;
    return L.error(e);
  }
};

AdView.prototype.render = function() {
  var e, error;
  L.debug((this.getMyName()) + ".render -> triggered");
  try {
    $("main").html(this.$el.html(this.compiledTemplate));
    window.scroll(0, this.scrollYPosition);
    L.debug((this.getMyName()) + ".render -> process completed.");
    return this;
  } catch (error) {
    e = error;
    return L.error(e);
  }
};
return AdView;

Solution

  • In your AdView.prototype.render method you have window.scroll(0, this.scrollYPosition);. It's not clear from the provided code where AdView.prototype.render gets called, but presumably something in your routing logic calls it when the user visits the route.

    You wrote :

    when pressing the browser back button there is an autoscrolling from the browser going back to the position of the listView before actually going back to the list view

    When you click the browser's back button that triggers a new routing operation, so it re-triggers that routing logic. Since your render logic involves changing the window's scroll position, it's only natural that when you click the back button you will re-trigger that render, which will re-change the scroll position.