2020.2.13

SPAページへのブラウザバックでスクロール位置を復帰する方法

ブラウザ操作(戻る、進む)によるSPAページと非SPAページ間の画面遷移で、vue.jsを使ったSPAページへ遷移する際にコンテンツの内容をSPAで生成していたため、遷移前のスクロールの位置がコンテンツ生成前のheightを超えていると、復帰した際のスクロール位置がおかしな状態になっていました。

今回はその対応のメモになります。

目次

  • 構成
  • スクロールの位置が復帰しない原因と対策
  • 対応方法(コード)
  • 最後に

構成

問題のあった構成は大きく分けて「ホーム」「検索(SPA)」「記事1、記事2、記事3 ...」の3つで、検索ページのみSPAで作成しているサイトになります。

簡単に図にすると以下の通り。

ホームと記事は静的なHTMLで、ホームからブラウザの「進む」で検索ページへ遷移、または記事からブラウザの「戻る」で検索ページへ遷移、といった場合のスクロールの復帰についての対応になります。

なお、Vue.jsを使ったSPAページ内のスクロールの復帰については、こちら(スクロールの振る舞い | Vue Router)を参照してください。

今回はあくまで、サイトの一部にSPA(Vue.js)ページを組み込んだ場合の話です。

スクロールの位置が復帰しない原因と対策

SPAページへのブラウザバックでスクロールの位置が正常に復帰しない原因は、SPAの処理でコンテンツを生成する前にブラウザ標準のスクロールの復帰処理が実行されてしまっているせいです。そのため、すべてのページが静的HTMLだったり、サーバ側でコンテンツを生成している場合は、こういった問題は起きません。

となると、コンテンツ生成後にスクロールの位置を復帰する処理を実行するようにすればいいという話になるのですが、残念ながらブラウザ標準のスクロール復帰処理を意図的に遅らすことはできないですし、ページ遷移前にセッションストレージなどでスクロールの位置を保存するとしても、直前の1ページだけならまだしもすべてのURLに対して保持するのは大変で、その上、ブラウザの履歴と連動させるのは至難の技です。

他にいい方法はないかと悩んで試した挙句にたどり着いたのは、SPAで生成したコンテンツを置き換えている要素に対して、最初は極力大きなheight値を設定しておく、という単純なものでした。

応用が効く方法かどうかは怪しいですが、今回は検索ページの一覧部のみSPAで生成しており、レスポンシブデザインでheight値が変動してしまうとはいえ、1ページの最大表示件数が決まっていたので、このような方法で対応することができました。

また、Vue.jsの場合、SPAコンテンツを埋め込むと置き換えている元々あった要素が削除されるので、この挙動を利用することで、CSSに1行追加するだけで済み、展開後のheight(高さ)も自動で勝手に調整されます。

対応方法(コード)

以下のコードはあくまでサンプルです。

HTML


<div class="search-area">
    <div id="vue-search-app">
      <span>Now Loading...</span>
    </div>
</div>

JavaScript


const routes = [
    { path: '/list' },
    { path: '/list/:category' },
];

const router = new VueRouter({
    mode: 'history',
    routes
});

window.onload = function () {
    const app = new Vue({
        el: '#vue-search-app',
        router,
        template: '<div>sample</div>',
    });
}

上記のHTMLとJavaScriptのコードはVue.jsの基本的なもので、重要なのは以下のCSSの設定になります。


#vue-search-app{
    height: 20000px;
}

※「height」にはそのページの最大の高さを設定してください(レスポンシブであればモバイルで表示した時の最大値)

Vueのコンテンツは指定した要素(#vue-search-app)と置き換わるので、最初は高さ「20000px」で表示されますが、コンテンツ展開後は、元々あった要素が削除されて、適切な高さに自動で調整されます。

やる前はスクロールの位置がカクついて見栄えがよろしくないかと思いましたが、実際にやってみると、カクつきはするもののページ読み込みの際はそこまで気にならず、スマートフォンでの表示の場合は、スクロールバーが細いのでまったく気になりませんでした。

最後に

上記の対応は画面の構成によっては使えないですし、スマートなやり方でもありません。他にやりようがなかったので仕方なく、といったものになるので、参考程度でお願いします。

そもそもサイトの一部のページにのみSPAを組み込むといったことは中々しないと思いますが...

ちなみに上記の対応は当サイトの検索ページに適用しています。今後、処理を変更する可能性もあるので、こちらも参考程度でお願いします。

Vue.js】関連記事