nuxt: anchors do not work

Version

v2.5.1

Reproduction link

https://codesandbox.io/s/mz4wjpzy18

Steps to reproduce

  1. click anchored link
  2. scroll back up
  3. click same anchored link => nothing happens

What is expected ?

working anchors accessible from any page (same or different)

What is actually happening?

no navigation is perfomed

<div align="right">This bug report is available on Nuxt community (#c8910)</div>

Additionnal comment

default scrollBehavior does not allow to reach anchors on the same page, but overriding it partially fixes the issue.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 18
  • Comments: 38 (5 by maintainers)

Commits related to this issue

Most upvoted comments

Is there any news on the standard and recommended (not a workaround) solution?

Been almost 2 years now, any update?

Any chance of re-opening this issue as it has not been fixed and, based on @folamy 's comment, seems to have gotten worse?

Have you tried hash: '#anchor'?

You can see the default scrollBehavior here and you can override this.

I typically use something like this:

  router: {
    scrollBehavior: async function(to, from, savedPosition) {
      if (savedPosition) {
        return savedPosition;
      }

      const findEl = async (hash, x = 0) => {
        return (
          document.querySelector(hash) ||
          new Promise(resolve => {
            if (x > 50) {
              return resolve(document.querySelector("#app"));
            }
            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 };
    }
  },

Thanks for your contribution to Nuxt.js! This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you would like this issue to remain open:

  1. Verify that you can still reproduce the issue in the latest version of nuxt-edge
  2. Comment the steps to reproduce it

Issues that are labeled as pending will not be automatically marked as stale.

the workaround I found is to combine to and v-scroll-to:

<nuxt-link :to="{path: '/', hash: 'anchor'}" v-scroll-to="{el: '#anchor'}">link</nuxt-link>

I’m also interested in seeing a “standard” solution to this.

Any news on this issue ? Facing it on v2.11.0

A workaround can be to use VueScrollTo library. Please look at https://github.com/rigor789/vue-scrollto/issues/251 issue status before using it (work only as a plugin for now)

Workaround for me is just adding a method to the click:

<nuxt-link
  ref="calculator"
  v-bind:to="{ path: '/', hash: '#calculator' }"
  v-on:click.native="scrollToSection('#calculator')"
>
scrollToSection(section) {
  if (this.$nuxt.$route.name === 'index') {
    const navHeight = document.getElementById('navigation').offsetHeight;
    window.scrollTo({
      top: document.querySelector(section).offsetTop - navHeight,
      behavior: 'smooth',
    });
  }
},

Of course you can add this method globally and just pass the section on buttons clicks.

Ok I had been struggling with this issue all day today, what I did is inserted the snippet I will leave below in my nuxt.config.js. The following way ` router: { scrollBehavior: async function(to, from, savedPosition) { if (savedPosition) { return savedPosition; }

  const findEl = async (hash, x = 0) => {
    return (
      document.querySelector(hash) ||
      new Promise(resolve => {
        if (x > 50) {
          return resolve(document.querySelector("#app"));
        }
        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 };
}

}`

Nor of the suggested solutions works in latest nuxt version v2.10.0 another thing that gives me concern is that I tried to log document.querySelector() in mounted and it returns null mounted () { console.log(document.querySelector('#loginView')) } result: null

Appears to be working fine now, after vue-router bug was fixed. I’ve taken the initial codesanbox example, exported it, updated Nuxt to 2.16.3, ran yarn upgrade and it worked as expected.

Looking at this, it’s an upstream issue in vue-router - see https://github.com/vuejs/vue-router/issues/1668 for description, reproduction and some workarounds. Nothing in scrollBehavior will be able to fix this because as far as vue-router is concerned, a route navigation hasn’t happened so scrollBehavior is never called.

saaame 👀

I think that might be fixed by this PR:

https://github.com/vuejs/vue-router/pull/3592

Isn’t it?

Since then, I have confirmed that it works normally with nuxt v2.15.8 by the following method. ・Url input

http://localhost:3000/#test

・ Anchor in the page

<a href="#test"> move to test </a>

I would appreciate it if you could refer to it.

◆ app / router.scrollBehavior.js

/*
 ** nuxt-link Top Scroll
 */
export default function (to, from, savedPosition) {
  if (savedPosition) {
    return savedPosition
  } else if (to.hash) {
    return {
      selector: to.hash,
    }
  } else {
    return { x: 0, y: 0 }
  }
}

◆ Target page.vue

<template lang="pug">
〜omit〜
  #test TEST
〜omit〜
</template>

  watch: {
    $route() {
      if (this.$route.hash) {
        setTimeout(() => this.scrollTo(this.$route.hash), 1)
      }
    }
  },
  mounted: function() {
    if (this.$route.hash) {
        setTimeout(() => this.scrollTo(this.$route.hash), 1)
    }
  },

  methods: {
    scrollTo: function(hash) {
      setTimeout(() => {
        location.href = hash
      }, 1)
    },

Have you tried hash: '#anchor'?

You can see the default scrollBehavior here and you can override this.

I typically use something like this:

  router: {
    scrollBehavior: async function(to, from, savedPosition) {
      if (savedPosition) {
        return savedPosition;
      }

      const findEl = async (hash, x = 0) => {
        return (
          document.querySelector(hash) ||
          new Promise(resolve => {
            if (x > 50) {
              return resolve(document.querySelector("#app"));
            }
            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 };
    }
  },

Thank you so much! This worked perfectly for me.

Also running into this issue with 2.5.1, but trying this workaround here for now: https://forum.vuejs.org/t/how-to-handle-anchors-bookmarks-with-vue-router/14563/5 (See 2nd last comment for solution)