6

I have the following code to save my data in current page state.

window.history.pushState({myData: data}, "RandomTitle", '#');

But now, I want to also save another value which is scrollTop. And if I do

window.history.pushState({scrollTop: scrollTop}, "RandomTitle", '#');

The code above will replace the history state with the new one and myData will be gone.

The question, Is there anyway I can add scrollTop while there's myData in my history state? Or I just have to set it initially like,

window.history.pushState({myData: data, scrollTop: 0}, "RandomTitle", '#');
// To change the scrollTop in history state
window.history.state.scrollTop = $(window).scrollTop();

I am thinking if there's a function like window.history.state.addObject or something.

choz
  • 17,242
  • 4
  • 53
  • 73

2 Answers2

4

By default, there is no history.state.addObject function. history.state contains the object that you pushed (and nothing else).

You may push something that has an addObject method, but it does not look like a good idea. If you want the change to be pushed in the history as a new state, you have to explicitly push the state each time anyway. And when you push a new state, you want to avoid mutating the previous one, which will happen if you use the same state object and modify it (even if according to the standard, history.state is supposed to be a clone of the actual state data). You should create a new object instead (it may be a clone of the previous state).

Here is how you can do it.

window.history.pushState(
  Object.assign({}, window.history.state, {scrollTop: scrollTop}),
  "RandomTitle", '#'
);

It will work even if there is no current state in the history (i.e. window.history.state is undefined) as Object.assign ignores undefined arguments.

You can also use this function that automates it:

function extendHistoryState(){
   arguments[0] = Object.assign({}, window.history.state, arguments[0]);
   window.history.pushState.apply(window.history, arguments);
}

It works exactly like pushState (same arguments) except that it will also copy the properties of the previous state in the new state. So instead of the previous code you can do:

extendHistoryState({scrollTop: scrollTop}, "RandomTitle", '#');
Quentin Roy
  • 7,677
  • 2
  • 32
  • 50
  • Thanks for your answer and btw, you're missing a coma after `Object.assign`. Now, If I happen to run this statement more than once, to replace my `scrollTop` or `myData` variables in history state, it doesn't work anymore. – choz May 03 '16 at 04:11
  • Right. Thank you. Updated. – Quentin Roy May 03 '16 at 04:12
  • The reason it was not working is because the value of the previous state had the priority over the new values you wanted to add (i.e. if the previous state contained a value, it was kept no matter what). I changed it by postponing these new values at the end of the `Object.assign` arguments. – Quentin Roy May 03 '16 at 05:16
  • I am having difficulties with your `extendHistoryState` function. And finally, I am able to extend my history state like `window.history.state = Object.assign({}, window.history.state, {scrollTop: value});`. But, this kind of approach will still need validation to check if there's an object in history state, otherwise I have to call it like `window.history.pushState` instead. if there's no better approach, I will soon accept this as an answer. – choz May 03 '16 at 05:25
  • Updating the state manually like you are doing (`window.history.state = something`) is a bad idea. If what you want is to replace the current state without pushing something new, use `history.replaceState` instead of `history.pushState`. – Quentin Roy May 03 '16 at 05:57
  • What problem do you have with the `extendHistoryState` function ? – Quentin Roy May 03 '16 at 05:58
  • You don't need to check if there is an object in `history.state`. `Object.assign` works fine with `undefined` arguments. – Quentin Roy May 03 '16 at 06:01
  • Nvm, I figured that I had to pass 2 arguments to use `extendHistoryState` which the second one tends to be useless. Well, it does create a state history if there isn't one, but it also doesn't create a hash state. Or please do provide an example of using it when you're creating or appending a new state object into history. – choz May 03 '16 at 06:10
  • 1
    `extendHistoryState` is designed to get exactly the same arguments as `pushState`. `pushState` usually takes 3 arguments: the state object, the title, and the url (optional). If you want to add the hash tag, provide it as url using this last argument.. – Quentin Roy May 03 '16 at 06:14
0

Since your URL parameter is the same between both pushState methods, you're actually replacing it instead of appending a new state to the history. You'll need to either specify a different URL to keep the state data separate, or combine the objects and reassign it back up to the state

theaccordance
  • 889
  • 5
  • 13