Ember.js восстанавливает прокрутку истории, сбрасывает прокрутку по ссылке

Дэниел Ф. Пупиус описал проблему следующим образом:

Многие сайты ошибаются, и это очень раздражает. Когда пользователь перемещается с помощью кнопок браузера «вперед» или «назад», позиция прокрутки должна быть такой же, как и в прошлый раз, когда он был на странице. Иногда это работает правильно на Facebook, но иногда нет. Google+ всегда теряет позицию прокрутки.

Так что уже есть многочисленные вопросы по сбросу скролла в начало страницы при переходе на новую страницу. Ember.js Cookbook также показывает, как подключиться к Route.activate:

export default Ember.Mixin.create({
  activate: function() {
    this._super();
    window.scrollTo(0,0);
  }
});

Однако это решает только половину проблемы. Когда пользователь использует кнопки назад/вперед, положение прокрутки не будет восстановлено, а просто сброшено.

Есть довольно много попыток решить эту проблему, многие просто сохраняя положение прокрутки внутри экземпляра Ember.Controller, например, в этом статья. Однако это лишь частичное решение. Если контроллер используется несколько раз, будет сохранено только одно состояние прокрутки.

Как можно сохранить реализацию браузера по умолчанию для восстановления старого состояния прокрутки? Таким образом, ничего не делать, если Route.activate было вызвано изменением состояния истории html5?


person Bouke    schedule 06.05.2015    source источник


Ответы (3)


Я только что решил эту проблему в своем собственном приложении.

Если вы хотите использовать его только там, где это необходимо, просто используйте миксин:

ember g mixin remember-scroll

Затем в mixins/remember-scroll.js замените на:

import Ember from 'ember';

export default Ember.Mixin.create({

  scrollSelector: window,

  activate: function() {
    this._super.apply(this, arguments);
    var self = this;
    if( this.get('lastScroll') ){

      Ember.run.next(function(){
        Ember.$(self.scrollSelector).scrollTop(self.get('lastScroll'));
      });

    } else {
      Ember.$(this.scrollSelector).scrollTop(0);
    }
  },

  deactivate: function() {
    this._super.apply(this, arguments);
    this.set('lastScroll',Ember.$(this.scrollSelector).scrollTop());
  },

});

Если вы хотите, чтобы он автоматически смешивался со всеми маршрутами, сделайте следующее.

В Ember CLI создайте новый файл initialier.

ember g initializer remember-scroll

Затем в initializers/remember-scroll.js замените код на этот:

import Ember from "ember";

var rememberScroll = Ember.Mixin.create({

  scrollSelector: window,

  activate: function() {
    this._super.apply(this, arguments);
    var self = this;
    if( this.get('lastScroll') ){

      Ember.run.next(function(){
          Ember.$(self.scrollSelector).scrollTop(self.get('lastScroll'));
      });

    } else {
      Ember.$(this.scrollSelector).scrollTop(0);
    }
  },

  deactivate: function() {
    this._super.apply(this, arguments);
    this.set('lastScroll',Ember.$(this.scrollSelector).scrollTop());
  },

});

export function initialize(/* container, application */) {
  Ember.Route.reopen(rememberScroll);
}

export default {
  name: 'remember-scroll',
  initialize: initialize
};

Вы можете удалить первый миксин выше, если вы используете версию инициализатора.

Это должно заставить все ваши маршруты помнить последнюю позицию прокрутки (если вы были там раньше) и применять ее, когда вы вернетесь туда.

Хотя я бы рекомендовал не размещать его во всех маршрутах, поскольку пользователи могут не ожидать возврата к предыдущей позиции прокрутки, если прошло значительное количество времени с тех пор, как они в последний раз были там.

person JeremyTM    schedule 15.08.2015
comment
Я думаю, что это не запомнит две разные позиции прокрутки, если контроллер был посещен два раза? - person Bouke; 20.08.2015
comment
Этот миксин применяется к маршруту, а не к контроллеру. Он запоминает положение прокрутки, когда вы уходите с маршрута (см. раздел deactivate), а затем восстанавливает его, когда вы возвращаетесь. - person JeremyTM; 21.08.2015

Я нашел решение JeremyTM здесь в целом эффективным. Однако использование метода deactivate по моему опыту приводит к несоответствиям в значении, установленном в lastScroll при переходе, тогда как использование метода действия willTransition маршрута приводит к согласованному поведению набора.

Вот модификация его миксина, которая работает лучше для меня:

import Ember from 'ember';

export default Ember.Mixin.create({
  activate: function() {
    this._super.apply(this, arguments);
    var self = this;

    if(this.get('lastScroll')){
      Ember.run.next(function(){
        Ember.$(window).scrollTop(self.get('lastScroll'));
      });
    } else {
      Ember.$(window).scrollTop(0);
    }
  },

  actions: {
    willTransition: function() {
      this._super.apply(this, arguments);
      this.set('lastScroll', Ember.$(window).scrollTop());
    }
  }
});

Также обратите внимание, что это по-прежнему кажется мне частичным решением проблемы, поскольку оно не сбрасывает прокрутку при всех кликах по ссылкам (в отличие от навигации/изменения истории браузера). Это происходит при первом щелчке ссылки, которая загружает маршрут, но все последующие щелчки ссылки на этот маршрут также загружают предыдущую позицию прокрутки, тем самым не распознавая предполагаемое действие пользователя по загрузке маршрута без ссылки на его предыдущее поведение просмотра ( он же со сбросом положения прокрутки).

person Mark Hendrickson    schedule 29.06.2016

Установите этот аддон ember ember-router-scroll, чтобы сбросить прокрутку с возможностью восстановления истории прокрутки. Он использует API-интерфейс HistoryLocation браузеров, поэтому он работает сразу из коробки.

person Musaffa    schedule 23.08.2016