As of Nuxt.js release 1.4.2, the default scroll behavior does not work as expected when using element ID's as hash links in routes (example: about-us/#john).
For reference: Nuxt.js Default Scroll Behavior
When navigated to directly, meaning the user immediately enters through the hash appended route, the browser handles the scroll targeting to the element with the matching ID. This is the expected behavior and works perfectly on initial page loads for completely static pages.
Once the site has loaded, however, the site operates as a single page application (SPA) and the browser stops responding to route changes as those are now handled by the vue-router. This allows for quicker page loads and navigation within the site is more controllable, but the browser no longer handles scrolling to focus on element IDs specified in hash appended routes, which has potential for breaking sites utilizing this functionality.
The solution is to override the default router.scrollBehavior method from within the nuxt.config.js configuration object.
module.exports = {
/*
** Router configuration
*/
router: {
scrollBehavior: async (to, from, savedPosition) => {
if (savedPosition) {
return savedPosition
}
const findEl = async (hash, x) => {
return document.querySelector(hash) ||
new Promise((resolve, reject) => {
if (x > 50) {
return resolve()
}
setTimeout(() => { resolve(findEl(hash, ++x || 1)) }, 100)
})
}
if (to.hash) {
let el = await findEl(to.hash)
if ('scrollBehavior' in document.documentElement.style) {
return window.scrollTo({ top: el.offsetTop, behavior: 'smooth' })
} else {
return window.scrollTo(0, el.offsetTop)
}
}
return { x: 0, y: 0 }
}
}
}
This configuration override solves two problems. First, it applies smooth to the window.scrollTo action to allow the browser to handle smooth scrolling to the proper element if available.
window.scrollTo({ top: el.offsetTop, behavior: 'smooth' })
Second, it checks for the existence of the element several times (50 to be exact) over the course of several seconds. The default scroll behavior expects the content to be loaded by the time the scroll action is called, but default Nuxt sites load the framework and start initial render before the full content is loaded from the server or CMS. The default script will give up after the first miss, causing the page to stay focused at the top. Rather than giving up after the first failed attempt, this script continues to search the DOM for the expected element every 100 milliseconds for 5 seconds (approximately). There is in theory more programmatic ways to determine when the content has finished loading, but the cost of complexity likely outweighs the fringe cases this code does not cover.
const findEl = async (hash, x) => {
return document.querySelector(hash) ||
new Promise((resolve, reject) => {
if (x > 50) {
return resolve()
}
setTimeout(() => { resolve(findEl(hash, ++x || 1)) }, 100)
})
}
This approach works well in both Nuxt modes and gives a uniform user experience regardless of whether the SPA has finished loading or not.
Top comments (14)
Thanks @dimer191996 , this was very useful!! 💪
There are some issues with this article ... 🤔
First: I found same article posted on 2018.09.04 by @ZachCardoza and here is the url for it zachcardoza.com/post/nuxtjs-smooth...
Second: it is for old version of NuxtJS current Nuxt version is 2.15.3
Third: router.scrollBehavior property is deprecated in favor of using ~/app/router.scrollBehavior.js file, learn more: nuxtjs.org/api/configuration-route...
Try to write your own articles or at lease give credits to the original authors,,,,
Hey, great work on this article. I'm working on a Nuxt project atm and this will be super useful!
wow you can't image how awesome I feel when you say that. Send me your GitHub so i can take a look at your project. :)
I'm glad it was appreciated! It's a work project so unfortunately we don't have a public repo for obvious reasons. I'll see if I can find a personal project to use it on some time though!
tnx bro it's super usefull ❤️
happy coding
Wow, Thanks
I got this error when there is the anchor in url path, you know why?
Uncaught (in promise) Error: [vue-router] TypeError: Cannot read properties of undefined (reading 'behavior')
You are f"#$"n smart! This was killing me for hours. Can't believe they still haven't internally fix this issue. Specially with static pages and hash navigation, this is a nuisance.
Thx man
Same here!, I don't really know what they're waiting for . Thanks To you man for reading it !
Thank you! You've really helped me out 👍
thank you very much
dev.to/bayyash/comment/1d09d
Some comments have been hidden by the post's author - find out more