[VUE.JS]Vue Router使用時にページ内アンカーにリンクする方法

Vue.jsを使用しているプロジェクトで、ページ内のアンカーポイントにリンクするボタンの実装をしました。
単純に、

<a href="#xxxx">テストリンク</a>

でいいのかと思ったら、上記で実装した場合、アンカーに飛ぶには飛ぶのですが、ページのURLが/#xxxxになってしまいました。
リロードするまでは、正常に動作していたんですが、リロードしたら真っ白に!
Vue Routerの設定によっては、そもそもアンカーが動作しなかったりする場合もあります。
ということで、下記のように解決しました。

scrollIntoViewでアンカーポイントまでスクロールする

まずは、同ページ内のアンカーに遷移してみます。
javascriptを使用して移動させる方法です。
単純ですが、動作にブレが無くいい感じです。

// button part
<a class="btn-primary" @click="scrollToAnchorPoint('testBlock')">
    Scroll Test
</a>

// anchor point
<div ref="testBlock">
    In page anchor point
</div>

//  methods
<script>
export default{
    ...
    methods: {
        scrollToAnchorPoint(refName) {
            const el = this.$refs[refName]
            el.scrollIntoView({ behavior: 'smooth'})
        }
    }
}
</script>

Vue.jsでDOM要素にアクセスする場合、refで名前を付けます。
this.$refsで要素にアクセスが可能です。
behaviorでスクロールの仕方を定義しています。

他のページからアンカーポイントにリンクする

今度は外部ページからアンカーポイントに遷移させたい場合の方法です。
URLのハッシュを読み込んで移動するように実装します。

routerlinkにhashを付ける方法

下記のように記述します。

//not bind
<router-link to="/testpage#testBlock">
    Link to TestBlock in TestPage
</router-link>

//use bind
<router-link
    :to="{
        name: 'testPage',
        hash: '#testBlock'
    }"
>
    Link to TestBlock in TestPage
</router-link>

hashからアンカーポイントまでスクロール

URLで受け取ったハッシュから該当のポイントまでスクロールさせます。

export default{
    data: function(){
        return {
            hash: this.$route.hash,
        }
    },
    mounted() {
        this.$nextTick(function () {
            if (this.hash) {
                const refName = this.hash.replace('#', '')
                this.scrollToAnchorPoint(refName)
            }
        })
    },
    methods: {
        scrollToAnchorPoint(refName) {
            const el = this.$refs[refName]
            el.scrollIntoView({ behavior: 'smooth' })
        }
    }
}

this.$route.hashでハッシュを取得しています。
DOMが生成された後にscrollToAnchorPointを実行したいので、
this.$nextTick()を使用して読み込み完了時に実行しています。

画面で画像(image等)を多用している場合

遷移した先で画像などを多用している場合は、DOM生成時に位置ずれが発生し正確な場所にスクロールしません。
これはDOMの高さを指定してあげることで解決できます。
例)

<div style="height: 400px;">
    <img src="/image/test.png">
</div>

注意としては画像自体の幅高さではなく、外側の要素に対して高さを指定することです。
とはいえ、全部に高さを指定するのが不可能な場合もあると思います。
その場合は、関数の実行を遅らせることでも対応できます。

mounted() {
    this.$nextTick(function () {
        if (this.hash) {
            const refName = this.hash.replace('#', '')
            setTimeout(() => {
                this.scrollToAnchorPoint(refName)
            }, 100)
        }
    })
},

setTimeoutで実行を遅らせます。
遅らせる時間はサイトの量で調整してください。

5分で終わると思ってたアンカーポイントの実装に、以外と苦労したので記事にしてみました。

VUE.JSおすすめの書籍

書籍は読む方だと思いますが、やはり現場で実装するとより深く学べるのを実感します。
逆に実装中心だと、より良いコーディングを考えることができなくなるので書籍もあらためて読むことをおすすめします。

これからはじめるVue.js実践入門
JavaScript、Vue.jsの知識がある程度ある方向け

Vue.jsのツボとコツがゼッタイにわかる本
初心者向け

[VUE.JS]Vue Routerでのデータの受け渡しの方法 - D-NET

[VUE.JS]Vue Routerでのデータの受け渡しの方法 - D-NET

[…] →参考[VUE.JS]Vue Router使用時にページ内アンカーにリンクする方法 […]

Comments are closed.