NuxtLink - scrollBehavior broken by back navigation
Environment
- Operating System:
Linux - Node Version:
v16.14.2 - Nuxt Version:
3.0.0-rc.3 - Package Manager:
[email protected] - Builder:
vite - User Config:
- - Runtime Modules:
- - Build Modules:
-
Reproduction
https://stackblitz.com/edit/nuxt-starter-xkacmx?file=app/router.options.ts
-
Scroll the main page a little bit.
-
go to "Long Page"
-
go back - the "savedPosition is correct.
-
now go to "Short Page"
-
go back - the home-page scrolled to the top.
Describe the bug
If the page, linked by NuxtLink has shorter content as the original page - by navigation back to original page the router not accepting the savedPosition (the savedPosition showing correctly by console.log(savedPosition) ) . So the original page is then scrolling to the top.
Expected behavior would be that original-page is staying by "savedPosition".
This Router settings where used:
import type { RouterConfig } from '@nuxt/schema';
// https://router.vuejs.org/api/#routeroptions
export default <RouterConfig>{
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
console.log(savedPosition);
return savedPosition;
} else {
return { top: 0, left: 0 };
}
},
};
Additional context
here is the proof that this is working with plain vue router - https://stackblitz.com/edit/vue-8fe51f?file=src/router.js but I am wondering what's then wrong with NuxtLink ?
Logs
No response
I have the same issue here. Although it persists in rc-3 it was already there in rc-1. I'm using a similar solution but it is kinda glitchy. The scrollBehavior() is called before the route changes causing the page to scroll to the top before it goes to another page, hence the glitchy experience.
I also noticed that the first page scrolls to top first before going to second one.
Howevere interesting is why the home-page not accepting "savedPosition" if the second page had shorter content as the one from home.
By returning to home-page I see correct data in the console, like {left: 0, top: 300}, but it looks like, it is not taken, like the "short" page resets something in router .
Exactly. My best guess is that happens because the scroll position that is passed along to the shorter page is not possible, therefore it goes to the closest top position. That is 0. The scroll becomes 0 again and so do the pages that you visit after too, until you change them again. If that makes sense.
I played around with this issue. If we set the return position always to top:100
scrollBehavior(to, from, savedPosition) {
return { top: 100, left: 0 };
},
In this case we assume that the page allways scrolls to 100 (except the short one ofcourse ). This is working fine with plain vue-3 and vue-router - ( https://stackblitz.com/edit/vue-hbr3jg?file=src%2Frouter.js )
But in Nuxt 3 the short page reset the top position to "0" by accesing the short page. So this "scollBehavior" seems not to play a role in this case at all.
Could it be that displaying of the new page recreates the router taking its current top position?
I also tried to modify the router.mjs of nuxt3 package directly to this:
nuxtApp.vueApp.component("NuxtPage", NuxtPage);
nuxtApp.vueApp.component("NuxtNestedPage", NuxtPage);
nuxtApp.vueApp.component("NuxtChild", NuxtPage);
const baseURL = useRuntimeConfig().app.baseURL;
const routerHistory = process.client ? createWebHistory() : createMemoryHistory(baseURL);
const initialURL = process.server ? nuxtApp.ssrContext.url : createCurrentLocation(baseURL, window.location);
const router = createRouter({
scrollBehavior(to, from, savedPosition) {
return { top: 100, left: 0,}
},
history: routerHistory,
routes
});
nuxtApp.vueApp.use(router);
return;
so to pass settings directly to router. The result is the same ....
any news on this?
Is there any chance there would be some work at this issue? It's still the same at RC6. :( I thinks it's a major bug, because it completely takes dows the user experience for navigation between pages!
I also feel the bug, it's very annoying. It's still the same at RC11. The scrollBehavior function executes before page render, so, if wait around 50ms by setTimeout, page will has right position. But it isn't solution, because different devices has different render duration, so that we can't use average time. https://stackblitz.com/edit/nuxt-starter-8lhh5x?file=app/router.options.ts
I hope this bug won't be included into release. I agree, it should be a major bug.
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.$router.options.scrollBehavior = () => {
return { left: 0, top: 0 }
}
})
Try this?
I think it is all about how much time it takes to wait for page transition, data fetching, rendering
It would be better if there is some state or hook to tell vue-router that " hey man I'm ready for scroll behavior " right now it fires way too early
at the moment, I'm using store to save "scrollReady",
- when leaving routes, set it false
- onMounted and when I'm really ready, set it true.
- Inside scrollBehavior function, await till scrollReady to be true.
I'm relly tired of all those "await until(some state).toBe(true)" and calling scroll-ready
Please, if there is smarter, better way, I would be so happy to learn...
below is my code
export default <RouterOptions> {
async scrollBehavior(to, from, savedPosition) {
console.log('scroll behavior')
// I set scrollFn in pageMeta for some pages where it should't do any scroll
const scrollFn = to.meta.scrollFn as Function || function () { return 1 }
if (scrollFn(to, from) !== 1) {
console.log('return null due to page meta scroll function')
return null
}
// set delay and default position, 300 is my page transition duration
const delay = savedPosition
? parseInt(to.meta.scrollDelay as string) || 300
: 0
const position = savedPosition || { top: 0, left: 0 }
// if not $pinia just return default top 0 left 0
const { $pinia } = useNuxtApp()
if (!$pinia)
return position
// wait for pages to call "I'm ready"
const { scrollReady } = storeToRefs(useBodyStore($pinia))
await until(scrollReady).toBe(true)
// wait for delay and return position
return new Promise((resolve) => {
setTimeout(() => {
return resolve(position)
}, delay)
})
},
}
Would you check with the latest edge channel; this may well have been resolved with https://github.com/nuxt/framework/pull/3851.
@danielroe, yes, it works as expected in edge channel! Waiting for RC13 👀 Thank you https://stackblitz.com/edit/nuxt-starter-dv9gfi
Great. Closing this. If you do encounter a problem with edge, please let me know and I'll happily reopen.