window.onpopstate, event.state == null?
Asked Answered
R

8

49

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?

Regelation answered 19/6, 2012 at 0:4 Comment(2)
possible duplicate of HTML history state issues on OS X Chrome 10.0.648.151Hayott
@Derek the question you provided is talking about popstate firing upon pageload. To clarify, at any time popstate fires, none of the data sent from a pushState function (ie. lastUrl in the above example) is available in the event object, this is the issue I'm inquiring aboutRegelation
F
69

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.

Farina answered 7/8, 2012 at 10:55 Comment(4)
Solved my problem I asked here #13107647 ... If you can write an answer for my question here, I can accept it.Jeromyjerreed
General comment: I would recommend instead of overloading the function window.onpopstate = function (e) { simply "listen" to it window.addEventListener("popstate", function(event) {... because otherwise you'll cancel other implementation is has which you didn't re-write.Ipecac
See my description of my problem with this same issue below. I did call the pushState many times every time the user (me) clicked a link. But the state was still always null. It only became non-null when I called pushState () "at least twice" IN THE ONLOAD-handler. I don't understand why calling it many times from the onclick -handler did not make it work. Did it perhaps have something to do with the 3rd argument to pushState() having a wrong value? Who knows. Perhaps. But calling the pushState() twice in the onload -handler solved the problem.Spiel
Just to add to the answer above: instead of calling pushState twice, which adds 2 entries to history, I prefer to call replaceState w my desired state upon load. Then subsequent pushState calls work as expected.Manet
C
21

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).

Circumferential answered 3/11, 2015 at 18:48 Comment(0)
S
4

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.

Spiel answered 26/5, 2018 at 2:23 Comment(1)
In my case popState would receive null state often (not always). Ensuring that there was an additional initial pushState call seems to have resolved the issue.Telesthesia
P
3

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)
Perrie answered 9/8, 2021 at 13:6 Comment(0)
S
2

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

enter image description here

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.

Swiss answered 21/2, 2019 at 9:7 Comment(1)
And this explains the null value how?Muddle
S
2

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.

Savanna answered 5/12, 2021 at 20:44 Comment(0)
G
0

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);
  }
});
Goree answered 17/3, 2020 at 19:5 Comment(0)
S
0

This is what I used

history.replaceState({myData: 'myData'}, '', '');
history.pushState({}, '', '');

When the back button is pressed, the last state is removed. You don't get that in the event. What you get is the second last state, the one you were at prior to pushing the state. Adding your data there means you will have it when you come back to it after popstate is fired.

Stand answered 12/7 at 4:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.