From d8d89b9dee7adf3f1a67cbbc23c93c7f85e29a4b Mon Sep 17 00:00:00 2001 From: Damian Glowala Date: Sat, 14 Sep 2024 10:31:16 +0200 Subject: [PATCH] fix(nuxt): wait for `page:finish` hook in CSR before scrolling to hash on page enter --- .../nuxt/src/pages/runtime/router.options.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/nuxt/src/pages/runtime/router.options.ts b/packages/nuxt/src/pages/runtime/router.options.ts index a415cc52f3..26d8fad402 100644 --- a/packages/nuxt/src/pages/runtime/router.options.ts +++ b/packages/nuxt/src/pages/runtime/router.options.ts @@ -29,25 +29,28 @@ export default { // Hash routes on the same page, no page hook is fired so resolve here if (to.path === from.path) { - if (from.hash && !to.hash) { - return { left: 0, top: 0 } + if (!to.hash) { + if (from.hash) { + return { left: 0, top: 0 } + } + + // The route isn't changing so keep current scroll position + return false } - if (to.hash) { - return { el: to.hash, top: _getHashElementScrollMarginTop(to.hash), behavior } - } - // The route isn't changing so keep current scroll position - return false } // Wait for `page:transition:finish` or `page:finish` depending on if transitions are enabled or not const hasTransition = (route: RouteLocationNormalized) => !!(route.meta.pageTransition ?? defaultPageTransition) const hookToWait = (hasTransition(from) && hasTransition(to)) ? 'page:transition:finish' : 'page:finish' + return new Promise((resolve) => { nuxtApp.hooks.hookOnce(hookToWait, async () => { await new Promise(resolve => setTimeout(resolve, 0)) + if (to.hash) { position = { el: to.hash, top: _getHashElementScrollMarginTop(to.hash), behavior } } + resolve(position) }) }) @@ -57,11 +60,13 @@ export default { function _getHashElementScrollMarginTop (selector: string): number { try { const elem = document.querySelector(selector) + if (elem) { return (Number.parseFloat(getComputedStyle(elem).scrollMarginTop) || 0) + (Number.parseFloat(getComputedStyle(document.documentElement).scrollPaddingTop) || 0) } } catch { // ignore any errors parsing scrollMarginTop } + return 0 }