AJAX内容和浏览器导航按钮

di3*_*sel 5 html javascript ajax browser-history html5-history

嗨有以下jQuery事件:

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

其中加载通过AJAX动态内容,并推动历史的国家,从修改浏览器的URL www.domain.comwww.domain.com/page-x.通过AJAX加载内容的唯一原因是我需要动画页面过渡.如果你去找www.domain.com/page-x你正常的HTML页面.

如果单击listItem用户单击其浏览器上的后退按钮后,会出现此问题.网址会更改www.domain.com/page-xwww.domain.com,但页面不会重新加载.这就是我添加此事件的原因:

window.onpopstate = function() {
  if (reload == true) {
    reload = false;
    location.reload();
  }
}
Run Code Online (Sandbox Code Playgroud)

如果当前页面是通过AJAX加载的,它会在url更改后重新加载页面.它工作正常,但现在我有其他问题:

  1. 用户在首页点击列表项;
  2. 浏览器URL更改,内容通过AJAX和动画加载;
  3. 用户点击BACK浏览器按钮;
  4. 浏览器URL更改为上一页并重新加载页面;
  5. 用户单击FORWARD浏览器按钮,URL更改但没有任何反应 ;

任何想法/解决方案都将受到高度赞赏.

Peb*_*bbl 5

而不是使用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);
  ...
}
Run Code Online (Sandbox Code Playgroud)

然后:

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);
}
Run Code Online (Sandbox Code Playgroud)

当用户回击时,您可能应该避免整页重新加载,而是将您的内容重新设置为曾经存在的内容,这样您就不会破坏页面.然后,您可以通过检查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.
  }
}
Run Code Online (Sandbox Code Playgroud)

我确定你知道这一点,但pushState并不是完全跨浏览器,所以如果不支持这些方法,你还应该预测用户会发生什么.


进一步增强

此外,当您使用jQuery时,它使您可以轻松地在状态对象中存储更多有用的信息,这可以帮助您增强您的反应popstate:

history.pushState({
  action: 'list-item-focused',
  listindex: jQuery(this).index()
});
Run Code Online (Sandbox Code Playgroud)

您必须记住,您存储的任何数据都是序列化的,这意味着它很可能会转换为某种形式的非交互式字符串或二进制数据.这意味着您无法存储对元素或其他"实时"实例的引用; 因此,事实上我正在存储列表项索引.

通过上面的内容,您现在可以了解正在执行的操作以及可以在另一端使用以下内容检索的列表项:

listItems.eq(event.state.listindex)
Run Code Online (Sandbox Code Playgroud)


怎么做onload?

MDN有这个说明你应该做什么onload.

阅读当前状态

页面加载时,它可能具有非null状态对象.例如,如果页面设置了一个状态对象(使用pushState()replaceState()),然后用户重新启动她的浏览器,就会发生这种情况.页面重新加载时,页面将收到onload事件,但没有popstate事件.但是,如果您读取history.state属性,您将获得一个状态对象,如果一个popstate被触发,您将获得该状态对象.

您可以在不等待使用history.state属性的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.
  }
}
Run Code Online (Sandbox Code Playgroud)

为简单起见,我使用了内联事件监听器,因为你的例子也是如此,但是建议使用addEventListenerattachEvent(仅限IE)方法......或者更好,因为你使用的是jQuery.

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


切换状态

显然,为了在两个州之间移动,你需要知道这两个州是什么; 这意味着有一些方法来记录您当前的状态 - 这样您就可以与新状态进行比较并采取相应的行动.由于你只有两种状态,这可能不是必要的,但我会敦促你一直在思考比现在更复杂的可能性.

我不知道你的代码的布局,所以它很难推荐你应该放置变量和其他类似项目的地方.但是,无论您如何存储信息,它都不会改变以下事实:如果您这样做,它将使您的生活和代码更容易:

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;
}
Run Code Online (Sandbox Code Playgroud)

更好的是,如果你不反对切换语句,你可以使上面的内容更具可读性:

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;
}
Run Code Online (Sandbox Code Playgroud)