LoginSignup
3
0

More than 1 year has passed since last update.

モバイルのスクロール全体・一部禁止(Pull-to-refresh)のコンポーネントを作ってみた(React)

Last updated at Posted at 2022-06-09

はじめに

モバイル用のwebアプリを作成していまして、 リロードされると色々と困ると思ったのでせめてスクロールリロードは禁止にしようと思い作りました。
その時に画面全部のスクロール禁止は簡単だけど、一部分のスクロール許可は大変だなと思ったのでメモです。
※一部分のスクロール許可コンポーネントはコンボボックスには対応していません。

完成したコンポーネント・CSS

画面全部のスクロール禁止処理コンポーネント
AllScrollLock.jsx
import React, { useEffect, useCallback } from "react";

const AllScrollLock= React.memo(() => {
  /**
   * イベントリスナーの設定
   */
  useEffect(() => {
    // モバイルスクロール禁止処理
    document.addEventListener("touchmove", scrollNo, { passive: false });

    return () => {
      // イベントの設定解除
      document.removeEventListener("touchmove", scrollNo);
    };
  }, []);

  /**
   * モバイルスクロール禁止処理
   */
  const scrollNo = useCallback((e) => {
    e.preventDefault();
  }, []);

  return <></>;
});

export default AllScrollLock;

一部分スクロール許可のコンポーネント

AllowPartialScrolling.jsx
import React, { useEffect, useRef, useCallback } from "react";

const AllowPartialScrolling= React.memo((props) => {
  // スクロール可能DOM格納
  const scrollArea = useRef(null);

  useEffect(() => {
    scrollArea.current = document.getElementById("scroll_item");
    scrollArea.current.scrollTop = 1;

    // モバイルスクロール禁止処理
    window.addEventListener("resize", setFillHeight);
    window.addEventListener("touchmove", scrollNo, { passive: false });
    scrollArea.current.addEventListener("focusout", onFocusOut);
    scrollArea.current.addEventListener("scroll", scrollControl);
    scrollArea.current.addEventListener("touchstart", touchHandler, {
      passive: false,
    });

    return () => {
      // イベントの設定解除
      window.removeEventListener("resize", setFillHeight);
      window.removeEventListener("touchmove", scrollNo);
      scrollArea.current.removeEventListener("focusout", onFocusOut);
      scrollArea.current.removeEventListener("scroll", scrollControl);
      scrollArea.current.removeEventListener("touchstart", touchHandler);
    };
  }, []);

  /**
   * 高さを調整する処理
   */
  const setFillHeight = useCallback(() => {
    window.scroll(0, 0);
  }, []);

  /**
   * モバイルスクロール禁止処理
   */
  const scrollNo = useCallback((e) => {
    if (
      e.target.closest("#scroll_item") === scrollArea.current &&
      scrollArea.current.scrollTop !== 0 &&
      scrollArea.current.scrollTop + scrollArea.current.clientHeight !==
        scrollArea.current.scrollHeight
    ) {
      e.stopPropagation();
    } else {
      e.preventDefault();
    }
  }, []);
  /**
   * ピンチズームをしない処理(2本指で操作させないようにする)
   */
  const touchHandler = useCallback((event) => {
    if (event.touches.length > 1) {
      event.preventDefault();
    }
  }, []);

  /**
   * フォーカスアウトした時の処理
   */
  const onFocusOut = useCallback(() => {
    window.scroll(0, 0);
  }, []);
  /**
   * スクロールリロードさせない処理
   */
  const scrollControl = useCallback(() => {
    if (scrollArea.current.scrollTop === 0) {
      scrollArea.current.scrollTop = 1;
    } else if (
      scrollArea.current.scrollTop + scrollArea.current.clientHeight ===
      scrollArea.current.scrollHeight
    ) {
      scrollArea.current.scrollTop = scrollArea.current.scrollTop - 1;
    }
  }, []);


  return (
    <div className="scroll_control" id="scroll_item">
      <div className="scroll_children">{props.children}</div>
    </div>
  );
});

export default AllowPartialScrolling;

一部分スクロール許可のcss

scroll.css
/* モバイルスクロールローディング禁止 */
.scroll_control {
  overflow-y: scroll;
  height: 100vh;
  -webkit-overflow-scrolling: touch;
  padding: 1.5vh 0 24vh 0;
}

.scroll_children {
  min-height: 100vh;
  -webkit-overflow-scrolling: touch;
  overflow-y: scroll;
}

実装方法

  • 画面全体をスクロールする必要がない画面は画面全部のスクロール禁止処理コンポーネントをimportします。
  • 画面の一部分をだけスクロールしたい画面は一部分スクロール許可のコンポーネントをimportします。
例:画面全体スクロール禁止の場合
app.jsx
import React from 'react';
import AllScrollLock from "../components/test/AllScrollLock";
import Hoge1 from "../components/hoges/Hoge1";
import Hoge2 from "../components/hoges/Hoge2";
import Hoge3 from "../components/hoges/Hoge3";
// フォルダの階層は各々違うの書き換えお願いします。

export default function App(props) {
    return (
     <>
      <Hoge1 />
      <Hoge2 />
      <Hoge3 />
      <AllScrollLock/>
    </>
  );
}

使いたい所で置くだけで画面全部がスクロール禁止になります。

例:一部分のスクロール可能コンポーネント

app.jsx
import React from 'react';
import AllowPartialScrolling from "../components/test/AllowPartialScrolling";
import Hoge1 from "../components/hoges/Hoge1";
import Hoge2 from "../components/hoges/Hoge2";
import Hoge3 from "../components/hoges/Hoge3";
// フォルダの階層は各々違うの書き換えお願いします。

export default function App(props) {
    return (
     <>
     <AllowPartialScrolling>
       <Hoge1 />
       <Hoge2 />
       <Hoge3 />
     </AllowPartialScrolling>
    </>
  );
}

一部分許可の場合はスクロールしたいDOMをはさむとスクロール可能になります。

大変だった部分:一部分のスクロール可能コンポーネント

  • テキストボックス押下時にキーボード出現のため画面の高さが変更される部分の対応。
  • cssの上下余白調整

感想

ページ事にスクロールの許可ができるよになったのでよかったです。作成している時はパッケージがないのかと思いましたが、パッケージありましたね(笑)
作成後に知りました:joy:

記事:body-scroll-lock.js「すべてのブラウザでスクロールのロックしたるで」←こいつ超有能

※追記↑cssでスクロールできる場所を作成しなければいけない点は一緒でした。

参考にしたwebサイト

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0