Vue Routerでページ内リンクする方法!【画面遷移時にページトップへ】

Vue.js Vue.js

Vue Routerを使用しているプロジェクトで、ページ内リンクをつける方法について書いています。
ページ内リンクが設定されていない場合(“#hoge”でIDが指定されていない)は、ページのトップへスクロールする設定になります。

サンプルコードは、Vue CLIの4.5.14で作成したプロジェクトを使用して、下記のバージョンで書いています。

Vue.js 3.0
Vue Router 4.0

Vue Routerでページ内リンクをつけるには?

Vue Routerを初期設定のまま使うと、ページ内リンクが効かずに、スクロールした分そのまま画面遷移されます。
そのままの設定で動かすと、下記のように動きます。
Vue Routerの設定なしで、リンクで画面遷移

リンク元ページのサンプルコード

リンク元の1ページ目は下記のように作成しました。
router-linkタグを使用して、次のページにリンクをつけています。

<template>
  <div>
    <div class="sample-link-area">
      <router-link to="/second#second_link">Secondページのテスト2へ</router-link>
    </div>
    <div class="sample-link-area">
      <router-link to="/second#third_link">Secondページのテスト3へ</router-link>
    </div>
    <div class="sample-link-area">
      <router-link to="/second">Secondページのトップへ</router-link>
    </div>
  </div>
</template>
<style>
.sample-link-area {
  margin-bottom: 20em;
}
</style>

#second_link#third_linkが次のページへのページ内リンクです。

リンク先ページのサンプルコード

リンク先の2ページ目は下記のように作成しました。

<template>
  <div>
    <div id="first_link" class="sample-area"> 
      <h2>テスト1</h2>
      テストです!!!<br>
      テストです!!!<br>
      テストです!!!<br>
      テストです!!!<br>
      テストです!!!<br>
      テストです!!!<br>
      テストです!!!<br>
      テストです!!!<br>
      テストです!!!<br>
    </div>
    <div id="second_link" class="sample-area"> 
      <h2>テスト2</h2>
      テストです!!!SAMPLE2<br>
      テストです!!!SAMPLE2<br>
      テストです!!!SAMPLE2<br>
      テストです!!!SAMPLE2<br>
      テストです!!!SAMPLE2<br>
      テストです!!!SAMPLE2<br>
      テストです!!!SAMPLE2<br>
      テストです!!!SAMPLE2<br>
      テストです!!!SAMPLE2<br>
    </div>
    <div id="third_link" class="sample-area">
      <h2>テスト3</h2>
      テストです!!!TEST3<br>
      テストです!!!TEST3<br>
      テストです!!!TEST3<br>
      テストです!!!TEST3<br>
      テストです!!!TEST3<br>
      テストです!!!TEST3<br>
      テストです!!!TEST3<br>
      テストです!!!TEST3<br>
      テストです!!!TEST3<br>
    </div>
  </div>
</template>
<style>
.sample-area {
  margin-bottom: 10em;
}
</style>

2つ目と3つ目のdivタグにそれぞれid属性を付加して、リンクできるようにしました。
このままだと、スクロールが残ったままのリンクになるので、Vue Routerに対して次項の設定が必要になります。

ページ内リンクするためのrouter設定

Vue Routerのルーティングファイルは下記のように作成しました。
先ほどの2つのファイルをルーティングに追加して、Vue Routerに対してリンク後はスクロールするように設定しています。

import { createRouter, createWebHistory } from 'vue-router'
import First from '../views/First.vue'
import Second from '../views/Second.vue'

const routes = [
  {
    path: '/',
    name: 'First',
    component: First
  },
  {
    path: '/second',
    name: 'Second',
    component: Second
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
  scrollBehavior(to) {
    if (to.hash) {
      return { el: to.hash };
    } else {
      return { top: 0 };
    }
  }
})

export default router

今回重要な設定として、リンクした場合にページ内リンク先やページトップに飛ばしているのはscrollBehavior関数です。

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
  scrollBehavior(to) {
    if (to.hash) {
      return { el: to.hash };
    } else {
      return { top: 0 };
    }
  }
})

scrollBehavior関数で受け取る引数のtoには、ブラウザの戻る機能やリンクで画面遷移した時に、下記のようなオブジェクトが入っています。

{
    "fullPath": "/second#third_link",
    "path": "/second",
    "query": {},
    "hash": "#third_link",
    "name": "Second",
    "params": {},
        //---省略---
}

この中のhashにページ内リンク先が入っているので、to.hashが存在すれば、el: to.hashで、そのタグの位置に返します。
ページ内リンクがついていない場合は、top: 0で、ページトップに遷移させるようになります。

if (to.hash) {
  return { el: to.hash };
} else {
  return { top: 0 };
}

このため、{ top: 0 }のみを返すようにするとページ内リンクは無視して、毎回ページトップに遷移するようになります。

動作を確認する

動作を確認すると、ページ内リンクのそれぞれの位置に遷移して、何もページ内リンクを設定していない場合はページトップに遷移することが確認できました。

Vue Routerでページ内リンクする(トップページに戻る)

画面遷移時にアニメーションをつけたい

設定したscrollBehavior関数を下記のようにすると、ページ遷移するときにアニメーションします。
returnを返している箇所で、behaviorプロパティを設定して、’smooth’を設定してあげます。

scrollBehavior(to) {
  if (to.hash) {
    return { el: to.hash, behavior: 'smooth' };
  } else {
    return { top: 0, behavior: 'smooth' };
  }
}

実際に動作を確認すると、下記のように画面遷移時にアニメーションすることが確認できます。
Vue Routerで遷移する時にアニメーションする

終わりに

今回はVue Routerを使用して、ページ内リンクをつける方法と、ページのトップに遷移する方法を解説しました。
必要な設定としては、createRouterを使用している箇所にscrollBehaviorを用意することで実現可能になりました。

コメント

タイトルとURLをコピーしました