This should definitely be done using the History API since it is the only way of knowing for sure that the user went back to the page using the back button of the browser.
First of all, you want to prevent the default scroll restoration of the browser in order to handle it yourself. See this article for details.
if ('scrollRestoration' in history) history.scrollRestoration = 'manual'
Then you need to store the scroll position in the history.state
when the user leaves the page. This can be done on beforeunload using history.replaceState.
window.addEventListener('beforeunload', storeScrollPositionInHistoryState)
function storeScrollPositionInHistoryState() {
// Make sure to add lastScrollPosition to the current state
// to avoid removing other properties
const newState = { ...history.state, lastScrollPosition: scrollY }
// Pass the current location.href since you only want to update
// the state, not the url
history.replaceState(newState, '', location.href)
}
or with ES5
function storeScrollPositionInHistoryState() {
var newState = Object.assign({}, history.state, { lastScrollPosition: scrollY })
history.replaceState(newState, '', location.href)
}
Finally, whenever your page is ready to scroll back to its previous position, you check if history.state.lastScrollPosition
is defined and if it is the case, you restore the scroll position and remove it from the history.state
.
function restoreLastScrollPositionIfAny() {
if (typeof history.state.lastScrollPosition !== 'number') return
scrollTo(scrollX, history.state.lastScrollPosition)
const cleanState = { ...history.state }
delete cleanState.lastScrollPosition
history.replaceState(cleanState, '', location.href)
}
There are still one edge case that is not covered:
- The user might have resized the window after leaving the page which can result in an incorrect scroll position when going back to the page. Developers resize the window very often, but real users not that much. That's why I consider it as an edge case.
One way to solve it could be to also store the window size in the history.state
and avoid restoring the scroll position if the current window size doesn't match the one stored in the state.
Hope it helps.