サムネイルの画像

SPAの遷移にページ内遷移の為のscroll-behavior: smooth;が影響してしまう際の解決法

CSS

Vue3

概要

スムーズなページ内遷移を実現するために、scroll-behavior: smooth;を使うことがあるかと思います。しかし、SPA(Single Page Application)では、このプロパティが遷移時に意図しない動作を引き起こすことがあります。

本記事では、SPAでscroll-behavior: smooth;が影響する問題を解決する方法について説明します。

実現したい挙動

ページ内遷移では連続的にスムーズに移動し、ページ間の遷移はパッと一番上に切り替わる。

問題

まず、SPAでscroll-behavior: smooth;がどのような影響を与えるかを説明します。

SPAで何も指定せずにvue routerの設定をすると、前のページのスクロール量が残ってしまいます。下記の画像のようなイメージです。

本来は下記のようなイメージで遷移したいですよね

二つ目のイメージのように遷移するたびに一番上へ遷移するような設定をした際、下記のようにscroll-behavior: smooth;を指定していると二ページに遷移するときパッと切り替わって欲しいのにスムーズなスクロールが入ってしまいます。

.css

html {
  scroll-behavior: smooth;
}

一番上へ遷移するような設定に関しては下記を参考にさせていただきました。

解決策の提案

①aタグでページ内遷移を行う
②JavaScriptを使用してスクロール制御を行う

①aタグでページ内遷移を行う

そもそもaタグでページ内遷移を行う時の為にscroll-behavior: smooth;を指定するわけですが、その解決法を提案します。

  • aタグを押した瞬間だけCSSにscroll-behavior: smooth;を指定する
  • setTimeout等でscroll-behavior: auto;に戻す

A-tag.vue

<script setup lang="ts">
const smoothScroll = () => {
  document.documentElement.style.scrollBehavior = 'smooth';
  setTimeout(() => {
    document.documentElement.style.scrollBehavior = 'auto';
  }, 100);
}
</script>

<template>
  <a @click="smoothScroll" href="#anchor">↓ aタグです </a>

  <div id="anchor" class="page page4">
      <p>4</p>
  </div>
</template>

②JavaScriptを使用してスクロール制御を行う

aタグでidで指定した要素にページ内遷移する方法ではなく、JavaScriptでwindow.scrollToを使用してページ遷移を行う方法を使用する方法です。

JS.vue

<script setup lang="ts">
import { onMounted, ref } from 'vue';

const anchor = ref<HTMLElement | null>(null);

onMounted(() => {
  anchor.value = document.querySelector('#anchor');
});

const smoothScroll = () => {
  const element = anchor.value;
  if (element) {
    window.scrollTo({
      top: element.offsetTop,
      behavior: 'smooth'
    });
  }
}
</script>

<template>
  <button @click="smoothScroll">↓ Clickイベントです </button>

  <div id="anchor" class="page page4">
      <p>4</p>
  </div>
</template>

scroll-behavior: smooth;の代わりにJavaScriptを使用して、ページ遷移時のスムーズスクロールを制御します。

実際に実装してみた

実際に両方見てみても挙動に差は見られません。

開発者ツールでソースを確認すると、aタグで遷移する方のページではaタグを押した際に<html>タグでstyleの変更が行われているのが見えると思います。

どちらを使うのがいいの?

私個人の感想になりますが、②のJavaScriptを使用してスクロール制御を行う方が違和感がないかなという気がしています。クラスの付け替えを頻繁に行うのはそういう動きが必要でない場合には気持ち悪い感じがしますが、プロジェクトに合わせて選んでいただければと思います。私は今後②の方法を主に使用していくと思います。

最後に

今回の問題以外にもSPAならではの問題が多々あるので都度ベストプラクティスを求めて解決していきたいです。SPAでaタグを使用すると他にも弊害が起きていたので、現象の再現と解決策が見つかりましたらまた書きたいと思います。

最後までありがとうございました。