Skip to content

refactor(router-core): scroll-restoration minor performance cleanup #4990

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 19, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 25 additions & 32 deletions packages/router-core/src/scroll-restoration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function getSafeSessionStorage() {
return window.sessionStorage
}
} catch {
return undefined
// silent
}
return undefined
}
Expand Down Expand Up @@ -85,14 +85,14 @@ export const defaultGetScrollRestorationKey = (location: ParsedLocation) => {

export function getCssSelector(el: any): string {
const path = []
let parent
let parent: HTMLElement
while ((parent = el.parentNode)) {
path.unshift(
`${el.tagName}:nth-child(${([].indexOf as any).call(parent.children, el) + 1})`,
path.push(
`${el.tagName}:nth-child(${Array.prototype.indexOf.call(parent.children, el) + 1})`,
)
el = parent
}
return `${path.join(' > ')}`.toLowerCase()
return `${path.reverse().join(' > ')}`.toLowerCase()
}

let ignoreScroll = false
Expand Down Expand Up @@ -120,7 +120,7 @@ export function restoreScroll({

try {
byKey = JSON.parse(sessionStorage.getItem(storageKey) || '{}')
} catch (error: any) {
} catch (error) {
console.error(error)
return
}
Expand All @@ -132,7 +132,7 @@ export function restoreScroll({
ignoreScroll = true

//
;(() => {
scroll: {
// If we have a cached entry for this location state,
// we always need to prefer that over the hash scroll.
if (
Expand All @@ -157,18 +157,18 @@ export function restoreScroll({
}
}

return
break scroll
}

// If we don't have a cached entry for the hash,
// Which means we've never seen this location before,
// we need to check if there is a hash in the URL.
// If there is, we need to scroll it's ID into view.
const hash = (location ?? window.location).hash.split('#')[1]
const hash = (location ?? window.location).hash.split('#', 2)[1]

if (hash) {
const hashScrollIntoViewOptions =
(window.history.state || {}).__hashScrollIntoViewOptions ?? true
window.history.state?.__hashScrollIntoViewOptions ?? true

if (hashScrollIntoViewOptions) {
const el = document.getElementById(hash)
Expand All @@ -177,30 +177,24 @@ export function restoreScroll({
}
}

return
break scroll
}

// If there is no cached entry for the hash and there is no hash in the URL,
// we need to scroll to the top of the page for every scrollToTop element
;[
'window',
...(scrollToTopSelectors?.filter((d) => d !== 'window') ?? []),
].forEach((selector) => {
const element =
selector === 'window'
? window
: typeof selector === 'function'
const scrollOptions = { top: 0, left: 0, behavior }
window.scrollTo(scrollOptions)
if (scrollToTopSelectors) {
for (const selector of scrollToTopSelectors) {
if (selector === 'window') continue
const element =
typeof selector === 'function'
? selector()
: document.querySelector(selector)
if (element) {
element.scrollTo({
top: 0,
left: 0,
behavior,
})
if (element) element.scrollTo(scrollOptions)
}
})
})()
}
}

//
ignoreScroll = false
Expand Down Expand Up @@ -294,11 +288,10 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
const restoreKey = getKey(router.state.location)

scrollRestorationCache.set((state) => {
const keyEntry = (state[restoreKey] =
state[restoreKey] || ({} as ScrollRestorationByElement))
const keyEntry = (state[restoreKey] ||= {} as ScrollRestorationByElement)

const elementEntry = (keyEntry[elementSelector] =
keyEntry[elementSelector] || ({} as ScrollRestorationEntry))
const elementEntry = (keyEntry[elementSelector] ||=
{} as ScrollRestorationEntry)

if (elementSelector === 'window') {
elementEntry.scrollX = window.scrollX || 0
Expand Down Expand Up @@ -344,7 +337,7 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
if (router.isScrollRestoring) {
// Mark the location as having been seen
scrollRestorationCache.set((state) => {
state[cacheKey] = state[cacheKey] || ({} as ScrollRestorationByElement)
state[cacheKey] ||= {} as ScrollRestorationByElement

return state
})
Expand Down