简体   繁体   中英

window.onpopstate, event.state == null?

I've read several question/answers on Stack Overflow and Googled the issue, I can't seem to get the event.state to come back with anything but null .

I got that it wont work with the jQuery event, but when I do something like this:

window.onpopstate = function (e) {
     console.log(e.state)
}

With something along the lines of a jQuery.ajax success calling

history.pushState({ lastUrl: data.State }, data.Title, data.UrlKey);

Neither Chrome (v19) or Firefox (v12) return anything but null? Am I missing something? Is it just not fully implemented?

e.state refers to the second last state that was pushed. You need to have pushed state at least twice for e.state to not be null . This is because you should save the state when your site is loaded the first time and thereafter every time it changes state.

I think that this question needs a more clear explanation because the sentence "second last state that was pushed" in the other answers may lead to confusion.

When we do history.pushState we are pushing into the history stack a new State, that becomes the current one (as we can see from the navigation bar url.)

The window.onpopstate event means that the top history state is being popped out (taken away from the stack) , so the e.state will now point to the new new top state in the stack (as the navigation bar will point to the previous url).

I struggled with this same issue for many hours. Too many hours. The state-argument of onpopstate -handler was null even though I had called pushState() many times before I clicked the back-button.

I also observed that my onclick-handler which called pushState() caused the onpopstate -handler to be triggered. I believe onpopstate -handler should only get called due to user clicking on the back-button from what I read on the web. But that seemed not to be the case in my case.

Could there be a bug in the browser? Seems unlikely because I had the same or similar problem on both Chrome and FireFox. But possible. Or maybe there is a "bug in the spec" being too complicated to implement correctly. No public unit-tests showing how this should work.

Finally I arrived at a solution after which EVERYTHING started working. It was to put these two calls into my onload-handler:

pushState (myInitialState, null, href);
pushState (myInitialState, null, href);

So I have to make the same push-state() call TWICE in the onload-handler! After that my onpopstate -handler started getting arguments whose state was NOT null but a state I had previously passed as argument to pushState().

I don't really understand why it now works and why it didn't earlier when I called pushState ONLY ONCE.

I would like to understand why it works now but I already spent too much time with this getting it to work. If anybody has a reference to good example-code online which explains it that would be great.

It will be helpful if you have ever learnt about the undo/redo stack, as the picture shows below.

在此处输入图片说明

In fact, the history is kept in a stack like this. Your current state is from current stack item, and let's assume there is a pointer to it; when you call history.back() , the pointer go to older item of the stack, the onpopstate event's state is the same as history.state by the way. You will just get the item which pointer currently pointing at, which modified by history.replaceState or added by history.pushState .

This process also explains why history.length doesn't change, because it represents the whole stack's length. When you call history.go(1) , the pointer move back to the newer item.

When the pointer is at a middle position of stack, calling history.pushState will lead to all the items above the pointer popped, and new one added, also you can see history'length changes.

As Jaco Briers mentioned, for popstate to work the way we think it should work, we need to first store the initial state to return to when you click on the browser's Back button. Here's a sample implementation using jQuery:

Sample Code

$(window).on({
  // Store initial state
  'load': function() {
    window.history.pushState({
      'html': '<div id="ajax-content">' + $('#ajax-content').html() + '</div>'
    }, '', document.URL);
  },
  // Handle browser Back/Forward button
  'popstate': function(e) {
    var oState = e.originalEvent.state;
    if (oState) {
      $('#ajax-content').replaceWith(oState.html);
    }
  }
});

...

// Update AJAX content
var sUrl = 'https://www.example.com/category?id=123&format=ajax';
$.ajax({
  type: 'GET',
  url: sUrl,
  success: function(sHtml) {
    $('#ajax-content').replaceWith(sHtml);
    window.history.pushState({
      'html': sHtml
    }, '', sUrl);
  }
});

history.state is associated with the current page, so it's possible to forward and back . To get the first history's state, you can use history.replaceState to place in the state of the first history.

window.history.replaceState(currentState, document.title)

Wow, all the answers here are really confusing.

  • you need to push or replace the initial state.
  • popstate does not give you the second last state, but the last state.
  • there is certainly no need to push the initial state twice.

I think what's confusing to everyone is that event.state will hold whatever value you set as the first ( data ) parameter in the history.pushState() call. If you set it to null , then sure enough your event.state will be null as well.

Why don't you need to push the initial state?

The initial state is right there in your event, it just has no data set, so event.state will be null . What you are looking for to redirect back to the initial page is simply the document.location (because at the time of popstate , we are already at that location).

If you want full control, it is a good idea to replace the initial state with some meaningful data set, so you can be sure it's the state you expected without having to compare the document url.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM