2

I am using history.replaceState() to update the query params of my page without causing a page reload as suggested in this SO answer.

function setQueryParam ({ name, value }) {
  const params = new URLSearchParams(window.location.search)

  params.set(name, value)
  history.replaceState({}, '', decodeURIComponent(`${window.location.pathname}?${params}`))
}

I am also storing the scroll position of the user with the following line:

history.scrollRestoration = 'auto'

When navigating from one page to another, scrollRestoration works fine - the scroll position is maintained between pages. However, after I change the query params with my setQueryParam function, scroll restoration no longer works.

Why is this happening?

Note: the same code works fine outside of Svelte/Sapper, using HTML and JavaScript only.

Yulian
  • 4,544
  • 5
  • 46
  • 69

1 Answers1

3

As a client-side router, Sapper has to hijack scroll management & restoration a good deal to emulate the behaviour you normally get when you fully reload the browser on each page change.

To do that, it uses history's state to know the scroll position to restore.

When you're using history.replaceState, you're changing the state (it's the first argument you place to replaceState). And so, Sapper don't find its restore scroll data when you later pop the state.

You can try to manually preserve the history state like this:

// notice the first argument
history.replaceState(history.state, '', decodeURIComponent(`${window.location.pathname}?${params}`))

I don't think history.scrollRestoration actually has any effect in Sapper.

rixo
  • 18,198
  • 3
  • 35
  • 50
  • "I don't think history.scrollRestoration actually has any effect in Sapper." - it does because if I don't replace the state, the scroll position is maintained. – Yulian Jun 11 '20 at 10:28
  • But does it behave any differently if you change the value of `history.scrollRestauration`? What I mean is I don't think Sapper takes this option into account in the scroll management / restauration it implements. Have you tried to pass on the `history.state`? – rixo Jun 11 '20 at 10:36
  • You are right, I removed `history.scrollRestauration = 'auto'` and it still maintains the scroll position. Moreover, I tried putting `history.state` as the first parameter in `replaceState()` and now everything seems to be working as expected. There are 2 weird things, however, first, I thought that history.state is just a JSON object defined by the user, but apparently there's private information in it, or Sapper uses it behind the scenes. Second, I tried the same in another project of mine and everything worked fine even without passing `history.state`. Anyway, thanks a lot for your answer! – Yulian Jun 11 '20 at 10:44
  • 1
    You're right about `history.state`, there's no default browser behavior attached to it. I haven't dug any further into the code, but I'm almost certain it's Sapper putting something in it. Was your other project using Sapper too? Because it is possible to keep history data without `history.state`, in which case the functionality wouldn't be affected by `replaceState`. – rixo Jun 11 '20 at 11:03
  • Yeah, I intentionally tried with another project that uses the same versions of Svelte and Sapper. Anyway, thanks a lot again! You saved me a ton of time! – Yulian Jun 11 '20 at 11:20