Scroll Restoration in JavaScript

Maintain our own scroll position with JavaScript.

Browser handled

It turns out, we might not need to do anything manually. This is because the browser does this for us most of the time.

If the browser is handling this automatically but we'd like to do something special instead, we can turn it off:

window.history.scrollRestoration = 'manual'

Now that it's off, or if wasn't working for our special case anyway, how do we maintain scroll position ourselves?

Detect scroll

First, we need some container on the screen to scroll. Let's say we have a side nav as an aside that is fixed to the left-side of the page.

Let's add a scroll event handler:

  document.querySelector('aside').addEventListener('scroll', handleScroll)

Persist the scroll position

When there's a scroll event, we can capture the target, and we can measure how far a element has scrolled (vertically) with element.scrollTop.

Then we have to find a good place to persist this data. It needs to persist between refreshes. But it doesn't need to stick around long term. Users don't expect the scroll position to be maintained for most things when they come back a week later. A good in-between storage location is sessionStorage.

To persist:

function handleScroll(evt) {
  window.sessionStorage.setItem(STORAGE_KEY, evt.target.scrollTop)
}

The key is key

And what about this STORAGE_KEY variable? This is the identifier for the scroll position in the key-value store of sessionStorage. We can change the behavior of scroll restoration a lot by how we use the key.

If we store a single key, then we maintain scroll position over the entire site. This seems to work great for a global side nav.

If we had multiple pages or widgets, we could use more keys and store more at a time. For instance, if the scroll position was maintained per page, we might want to use window.location.pathname as a part of the key.

For now, we'll use:

const STORAGE_KEY = 'aside-scroll-position-y'

Retrieve and restore

Now that we have our scroll position saved for the side nav on every scroll event, we need to retrieve that position on every page load and set that position on the side nav:

window.addEventListener('load', () => {
  const y = sessionStorage.getItem(STORAGE_KEY) || 0
  document.createElement('aside').scrollTo(0, y)
})

And that's it. We have the scroll, the save and the restore.

See jaketrent/demo-scroll-restoration for the working example.

How else do you do scroll restoration?