1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Nuxt, Vue】NuxtLinkで同じセクションに遷移できない、リロードしてもセクションに遷移しない【VueRouter, vue-router】

Posted at

現象

NuxtLink:toを使用して画面内のナビゲーションを作成したが、一度項目を選択した後、画面をスクロールして別のセクションまで移動、その後もう一度同じ項目を選択すると、遷移が起きずスクロールした位置のままになってしまう。

期待値

再度選択した場合も、セクションに遷移したい。

原因

これはNuxtLink(Vue Router)の仕様らしい。
同じセクションをクリックしても遷移前とURLが変わらないため、遷移がスキップされてしまう。

解消方法

$router.push()を使用する & nuxt.config.jsを編集することで解決できる。

が、今回nuxt.config.jsに手を加えたくなかったので、遷移ではないが、画面内スクロールを起こすことで解決してみました。

<template>
    <NuxtLink
        :key="link.to"
        :to="link.to"
        @click.native="handleClick($event, link.to)"
    >
        {{ link.label }}
    </NuxtLink>
</template>
<script lang="ts">
methods: {
    handleClick(event: MouseEvent, to: string) {
      // すでに同じハッシュが指定されている場合はスクロールする
      const hash = to.startsWith('#') ? to : to.split('#')[1] && `#${to.split('#')[1]}`
      if (hash && window.location.hash === hash) {
        event.preventDefault()
        const el = document.querySelector(hash)
        if (el) {
          el.scrollIntoView({ behavior: 'smooth' })
        }
      }
      // それ以外はNuxtLinkのデフォルト動作
    },
}
</script>

これで、同じセクションがクリックされた場合はスクロールで指定されたセクションを表示してくれるようになります。

解決方法2

前述した$router.push()を使用する & nuxt.config.jsを編集する方法の場合
これはNuxt2限定のようです。Nuxt3の場合はapp/router.options.tsとのこと

<template>
<!-- NuxtLinkは同じ -->
</template>
<script lang="ts">
methods: {
    handleClick(event: MouseEvent, to: string) {
        const current = this.$route.fullPath
        const target = to.startsWith('#') ? `${this.$route.path}${to}` : to

        if (current === target || (to.startsWith('#') && window.location.hash === to)) {
            event.preventDefault()
            // 強制的に遷移させる
            this.$router.push(target)
        }
        // それ以外はNuxtLinkのデフォルト動作
    },
},
</script>
// nuxt.config.js
export default {
  // 他の設定は省略
  router: {
    scrollBehavior(to, from, savedPosition) {
      if (to.hash) {
        return new Promise((resolve) => {
          // DOMが描画されるまで待つ
          setTimeout(() => {
            const el = document.querySelector(to.hash)
            if (el) {
              el.scrollIntoView({ behavior: 'smooth' })
              resolve({ selector: to.hash })
            } else {
              resolve({ x: 0, y: 0 })
            }
          }, 300)
        })
      }
      if (savedPosition) {
        return savedPosition
      }
      return { x: 0, y: 0 }
    }
  }
}

この設定を加えると、リロードしても指定されたセクションまでスクロールしてくれます。
なので、$router.push()で再度同じURLに遷移すると、指定されたセクションを表示してくれるというわけですね。

おわりに

vue-routerさんは便利だけど、デフォルトでよしなにやってくれないこともあるので要注意ですね。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?