ページトップボタンを実装【React】

React JavaScript

Webページを開き、下へスクロールしながらコンテンツを見ていく。

ある程度スクロールしたらこんなことを思うのではないでしょうか?

  • 最初の位置までパパッと戻りたい
  • 上にスクロールして戻るのは面倒くさい

ユーザーのそんな気持ちに応えるのが「ページトップボタン」です。

今回はそんなページトップボタンをReactコンポーネントにし、その中身について解説していきたいと思います。

ソースコードだけ拝借したいという人は目次から「全コード」を見ていただきたいです。

ユーザーが下に少しスクロールしてから

ページトップボタンはいつ必要となるのか?それは「ユーザーが下に少しスクロールしてから」です。

まだスクロールしていない段階でページトップボタンがあっても邪魔です。また常時表示させてしまうと、メインビジュアルと被って見栄えに悪影響をもたらしてしまいます。

ユーザーが下に少しスクロールしてからページトップボタンが表示されるようにするには、以下のことが必要です。

  • ボタンを表示させるかどうかの状態を管理
  • スクロール量に応じて状態を更新
  • 状態に応じてボタンを表示/非表示

上記3つを実現するために、Reactフックを使って実装していきたいと思います。

宣言やインポート

ページトップボタンのコンポーネントに必要な宣言やインポートについて見ていきます。

"use client";

上記でuse client宣言。クライアント側で処理するフックを使うので、この宣言が必要です。

import { useState, useEffect } from "react";
import styles from "./index.module.css";

上記では使用するフックとモジュール化したCSSをインポート。

import文に関しては必要になってから記述すればいいよね?

そうだね。なんならテキストエディタの自動補完で勝手に書いてくれたりするから随時書いていく方がいいかもね。

export default function PageTop() {

PageTopコンポーネントを作成。これから中身を書いていきます。

PageTopコンポーネント

  • ボタンを表示させるかどうかの状態を管理
  • スクロール量に応じて状態を更新
  • 状態に応じてボタンを表示/非表示

上記3つを実現するコンポーネントを作っていきます。

ボタンを表示させるかどうかの状態を管理

  • ボタンを表示させるかどうかの状態を管理

上記はuseStateフックで行います。

const [isVisible, setIsVisible] = useState(false);

setIsVisibleというstateを作りました。名前は任意です。

あとで isVisible が true のときにボタンを表示、falseのときはボタンを非表示、となるようにすることで表示/非表示を制御していきます。

  • isVisible が true のとき、ボタンを表示
  • isVisible が falseのとき、ボタンを非表示

スクロール量に応じて状態を更新

  • スクロール量に応じて状態を更新

上記はイベントハンドラとuseEffectフックで行います。

useEffect(() => {
        const handleVisible = () => {
            if (scrollY > 500) {
                setIsVisible(true);
            } else {
                setIsVisible(false);
            }
        };
        addEventListener("scroll", handleVisible);
        return () => removeEventListener("scroll", handleVisible);
    }, []);

スクロール量によって先ほど作成した「setIsVisible」を更新するイベントハンドラを作ります。名前は「handleVisible」としました。

上からのスクロール量が500pxを超えたらsetIsVisibleをtrueに、超えるまではsetIsVisibleをfalseになるようにします。

  • 上からのスクロール量が500pxを超えたらsetIsVisibleをtrueに
  • 上からのスクロール量が500pxを超えるまではsetIsVisibleをfalseに

ここで作ったイベントハンドラはscrollYプロパティを使っています。scrollYプロパティはWebAPIの一つです。

WebAPIを用いて状態を更新することになるので、「副作用」が発生します。

そこで使われるのが「useEffect」です。useEffectを使うことで、コンポーネントの純粋性を保ちつつスクロール量に応じて状態を更新させられます。

スムーズにトップへ

const handleScrollTo = () => {
        scrollTo({
            top: 0,
            behavior: "smooth",
        });
    };

先ほどはスクロール量に関するWebAPIを使いましたが、ここではスクロール移動に関するWebAPI「scrollTo」を使います。

scrollTo() メソッドで最上部へスムーズに移動できるように指定しておきます。

状態に応じてボタンを表示/非表示

  • 状態に応じてボタンを表示/非表示

上記を実現するために、

return (
        <button
            onClick={handleScrollTo}
            className={`${styles.pageTop} ${isVisible ? styles.visible : ""}`}
            aria-label="ページトップへ戻る"
        >
            <span>TOP</span>
        </button>
    );

上記を出力させます。

onClick={handleScrollTo}

buttonタグにイベントハンドラ「handleScrollTo」を設置。クリックされたときの処理を操作します。

className={`${styles.pageTop} ${isVisible ? styles.visible : ""}`}

またisVisibleがtrueのときはvisibleクラスのcssが適応されるようにします。

そうすると、下記のようにcss側で

.pageTop {
  (省略)
    opacity: 0;
}
.pageTop.visible {
    opacity: 1;
}

pageTopクラスがデフォルトでは透明に、visibleクラスが付与されたら不透明になるようにしておくことで、ページトップボタンを表示させたり非表示にさせたりできます。

厳密には「非表示」ではなく「透明」にして実質非表示になるようにしています。displayプロパティの切り替えで「表示」「非表示」の切り替えをさせてもいいのですが、「transition: opacity 0.3s ease-in-out;」などで切り替え時にふわっとさせたいので、opacityプロパティの切り替えで「非透明(表示)」「透明(非表示)」の切り替えにしています。

}

これでPageTopコンポーネントが完成しました。

css

.pageTop {
    (省略)
    opacity: 0;
    transition: opacity 0.3s ease-in-out;
}
.pageTop.visible {
    opacity: 1;
}
.pageTop span {
    (省略)
    transform: translateY(20%) rotate(-45deg);
}

pageTopクラスをデフォルトでは透明にしておき、visibleクラスが付与されたら0.3秒をかけて不透明になるようにしています。

全コード

今回のソースコードを掲載しておきます。

index.tsx

"use client";

import { useState, useEffect } from "react";
import styles from "./index.module.css";

export default function PageTop() {
    const [isVisible, setIsVisible] = useState(false);

    useEffect(() => {
        const handleVisible = () => {
            if (scrollY > 500) {
                setIsVisible(true);
            } else {
                setIsVisible(false);
            }
        };
        addEventListener("scroll", handleVisible);
        return () => removeEventListener("scroll", handleVisible);
    }, []);

    const handleScrollTo = () => {
        scrollTo({
            top: 0,
            behavior: "smooth",
        });
    };

    return (
        <button
            onClick={handleScrollTo}
            className={`${styles.pageTop} ${isVisible ? styles.visible : ""}`}
            aria-label="ページトップへ戻る"
        >
            <span>TOP</span>
        </button>
    );
}

該当箇所に対するcss

.pageTop {
    display: flex;
    justify-content: center;
    align-items: center;
    position: fixed;
    width: 60px;
    height: 60px;
    background-color: #f6ad3c;
    border: 1px solid #e6e6e6;
    color: #000;
    bottom: 40px;
    right: 40px;
    cursor: pointer;
    z-index: 100;
    opacity: 0;
    transition: opacity 0.3s ease-in-out;
}
.pageTop.visible {
    opacity: 1;
}
.pageTop span {
    width: 32%;
    height: 32%;
    border-top: 3px solid #333;
    border-right: 3px solid #333;
    transform: translateY(20%) rotate(-45deg);
}

まとめ

ここまでページトップボタンを実装するためのReactコンポーネントについて解説してきました。

ページトップボタンを実装したデモサイトが完成したら、こちらに追記して掲載いたします。

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