AJAX контент и кнопки навигации браузера


У меня есть следующее событие jQuery:

listItems.on('click', function(ev) {
  ev.preventDefault();
  reload = true;
  var url = jQuery(this).attr('data-url');
  history.pushState({}, '', url);
  ...
}

, который загружает динамический контент через AJAX и толкает состояние истории для изменения URL в браузере с www.domain.com на www.domain.com/page-x. Единственная причина, по которой я загружаю контент через AJAX, заключается в том, что мне нужно анимировать переходы страниц. Если вы перейдете на www.domain.com/page-x, вы получите обычную HTML-страницу.

Проблема возникает, если после нажатия кнопки listItem пользователь нажимает кнопку Назад в своем браузере. Url-адрес изменяется обратно с www.domain.com/page-x на www.domain.com, но страница не перезагружается. Вот почему я добавил Это событие:

window.onpopstate = function() {
  if (reload == true) {
    reload = false;
    location.reload();
  }
}

Он перезагружает страницу после изменения url, если текущая страница была загружена через AJAX. Это работает нормально, но теперь у меня есть другая проблема:

  1. пользователь на странице frontpage щелкает элемент списка;
  2. изменяется URL браузера, контент загружается через AJAX и анимируется;
  3. пользователь нажимает кнопку Назад в браузере;
  4. URL браузера изменяется на предыдущий и страница перезагружается;
  5. пользователь нажимает кнопку переадресации браузера, URL изменяется, но ничего бывает ;

Любые идеи / решения будут высоко оценены.

1 5

1 ответ:

Вместо того, чтобы использовать reload=true, вы должны полагаться на event.state, чтобы при появлении popstate вы получили снимок того, что вы записали для этого URL.

Управление историей браузера (MDN)

Например:

listItems.on('click', function(ev) {
  ev.preventDefault();
  var url = jQuery(this).attr('data-url');
  history.pushState({ action: 'list-item-focused' }, '', url);
  ...
}

А затем:

window.onpopstate = function(e) {
  /// this state object will have the action attribute 'list-item-focused'
  /// when the user navigates forward to the list item. Meaning you can
  /// do what you will at this point.
  console.log(e.state.action);
}

Вероятно, вам следует избегать полной перезагрузки страницы, когда пользователь возвращается, и вместо этого анимировать свой контент, который раньше был там, таким образом, вы не уничтожаете страницу. Вы можете тогда сказать разницу между целевую страницу и страницы списка, проверяя наличие event.state.action и соответственно кодируя свои ответы.

window.onpopstate = function(e) {
  if ( e.state.action == 'list-item-focused') {
    /// a list item is focused by the history item
  }
  else {
    /// a list item isn't focused, so therefore landing page
    /// obviously this only remains true whilst you don't
    /// make further pushed states. If you do, you will have to
    /// extend your popstate to take those new actions into account.
  }
}
Я уверен, что вы знаете об этом, но pushState не является полностью кроссбраузерным, поэтому вы также должны предвидеть, что может произойти с пользователями, если эти методы не поддерживаются.


Дальнейшие усовершенствования

Кроме того, поскольку вы используете jQuery, это позволяет вам довольно легко хранить дополнительную полезную информацию в объекте состояния, что может помочь вам улучшить ваш реакции в popstate:

history.pushState({
  action: 'list-item-focused',
  listindex: jQuery(this).index()
});
Вы должны иметь в виду, что любые данные, которые вы храните, сериализованы, а это означает, что они, скорее всего, будут преобразованы в некоторую форму неинтерактивных строковых или двоичных данных. Это означает, что вы не можете хранить ссылки на элементы или другие "живые" экземпляры; поэтому я храню индекс элементов списка вместо этого.

Теперь вы знаете, какое действие происходило и на каком элементе списка, который вы можете получить на другом конце с помощью использование:

listItems.eq(event.state.listindex)


Что делать при загрузке?

MDN имеет это сказать о том, что вы должны сделать onload.

Чтение текущего состояния

Когда ваша страница загружается,она может иметь ненулевое состояние объекта. Это может произойти, например, если страница устанавливает объект состояния (используя pushState () или replaceState()), а затем пользователь перезапускает свой браузер. Когда ваша страница перезагрузится, она получит сообщение: onload событие, но нетpopstate событие. Однако, если Вы читаете историю.государственная собственность, вы получите обратно государственный объект, который вы получили бы, если быpopstate выстрелил.

Вы можете прочитать состояние текущей записи истории, не дожидаясь события popstate, используя историю .Состояние свойство, подобное этому:

Проще говоря, они просто рекомендуют вам прислушаться к текущему history.state, когда страница нагрузки, и действовать соответственно на основе того, что ваш state.action описывает. Таким образом, вы поддерживаете оба события, которые запускаются в течение жизненного цикла Страницы, т. е. popstate, и когда пользователь непосредственно переходит в состояние истории, которое вызывает загрузку страницы.

Таким образом, с учетом вышесказанного, вероятно, было бы лучше структурировать вещи следующим образом:

window.onpopstate = function(e) {
  reactToState(e.state);
}

window.onload = function(){
  history.state && reactToState(history.state);
}

var reactToState = function(state){
  if ( state.action == 'list-item-focused') {
    /// a list item is focused by the history item
  }
  else {
    /// a list item isn't focused, so therefore landing page
    /// obviously this only remains true whilst you don't
    /// make further pushed states. If you do, you will have to
    /// extend your popstate to take those new actions into account.
  }
}

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

jQuery(window)
  .on('popstate', function(e) {
    reactToState(e.originalEvent.state);
  }
  .on('load', function(){
    history.state && reactToState(history.state);
  }
;


Переключение состояний

Очевидно, чтобы перемещаться между двумя состояниями, вам нужно знать, что представляют собой оба этих состояния; это означает, что у вас есть какой - то способ записать ваше текущее состояние, чтобы вы могли сравнить его с новым состоянием и действовать соответственно. Поскольку у вас есть только два состояния, это может быть не обязательно, но я бы настоятельно рекомендовал вам всегда думать о возможности иметь больше сложность, чем вы сейчас обладаете.

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

var reactToState = function(state){
  var currentState = reactToState.currentState || {};
  if ( !currentState.action ) { currentState.action = 'start'; }
  if ( state.action == 'list-item-focused') {
    if ( currentState.action == 'start' ) {
      /// here we know we are shifting from the start page to list-item
    }
  }
  else {
    if ( currentState.action == 'list-item-focused' ) {
      /// here we know we are shifting from the list-item to the start
    }
  }
  /// as the state will be global for your application there is no harm
  /// in storing the current state on this function as a "static" attribute.
  reactToState.currentState = state;
}

Еще лучше, если вы не прочь переключить операторы, вы можете сделать вышеизложенное более читаемым:

var reactToState = function(state){

  /// current state with fallback for initial state
  var currentState = reactToState.currentState || {action: 'start'};

  /// current to new with fallback for empty action i.e. initial state
  var a = (currentState.action||'start');
  var b = (state.action||'start');

  switch ( a + ' >> ' + b ) {
    case 'start >> list-item-focused':
      /// animate and update here
    break;
    case 'list-item-focused >> start':
      /// animate and update here
    break;
  }

  /// remember to store the current state again
  reactToState.currentState = state;
}