DEV Community

Dimer Bwimba
Dimer Bwimba

Posted on

Nuxt.js Smooth Scrolling with Hash Links

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 }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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' })
Enter fullscreen mode Exit fullscreen mode

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)
  })
}
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
arukomp profile image
Arunas Skirius

Thanks @dimer191996 , this was very useful!! πŸ’ͺ

Collapse
 
bayyash profile image
Info Comment hidden by post author - thread only accessible via permalink

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,,,,

Collapse
 
katieadamsdev profile image
Katie Adams

Hey, great work on this article. I'm working on a Nuxt project atm and this will be super useful!

Collapse
 
dimer191996 profile image
Dimer Bwimba

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. :)

Collapse
 
katieadamsdev profile image
Katie Adams

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!

Collapse
 
mrehsanakbarzadeh profile image
Ehsan Akbarzadeh

tnx bro it's super usefull ❀️

Collapse
 
dimer191996 profile image
Dimer Bwimba

happy coding

Collapse
 
lucaargentieri profile image
Luca Argentieri

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')

Collapse
 
ed3899 profile image
Eduardo Casanova • Edited

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

Collapse
 
dimer191996 profile image
Dimer Bwimba

Same here!, I don't really know what they're waiting for . Thanks To you man for reading it !

Collapse
 
prostoleo profile image
prostoleo

Thank you! You've really helped me out πŸ‘

Collapse
 
jamaluddinrumi profile image
Jamaluddin Rumi

thank you very much

Collapse
 
bayyash profile image
Info Comment hidden by post author - thread only accessible via permalink
Bashar Ayyash

Some comments have been hidden by the post's author - find out more