image

상단 이동 버튼

태그
ReactJavascript
상세설명상단 이동 버튼 & 스크롤 시 버튼 나타나기
작성일자2023.10.17

포스트 글이 길거나 블로그, 프로젝트 갯 수가 많아지면 스크롤이 발생해 상단 이동 버튼을 배치하는게 좋을 것 같아 추가해보았다.

상단 이동 버튼

window.scrollTo(xpos, ypos, behavior:'auto'}) 를 사용하면 원하는 위치로 이동 시킬 수 있다.

behavior의 값에는 auto, instant, smooth가 있다. (문자이므로 따옴표가 필요하다.)

auto는 기본 값이며, 바로 위치로 이동한다. instant도 같은 동작을 한다.

smooth는 부드럽게 이동하는 애니메이션 효과를 보여준다.

⇒ 부드럽게 상단 위치하기 위해서 클릭 시 window.scrollTo({ top: 0, behavior: "smooth" }); 실행되게 했다.

import { useEffect, useState } from "react";
import { HiChevronUp } from "react-icons/hi";

export default function MoveToTop() {
  const moveToTop = () => {
    window.scrollTo({ top: 0, behavior: "smooth" });
  };

  return (
    <div
      onClick={moveToTop}
      className="fixed bottom-7 right-4 lg:right-10 p-2 z-20 cursor-pointer hover:scale-90 transition-all duration-500 rounded-full bg-[#2c82f2]"
    >
      <HiChevronUp className="text-2xl text-white" />
    </div>
  );
}

상단 이동 버튼 + 일정 스크롤 높이에서 등장

추가로 스크롤 브라우저 최 상단에서는 상단 이동 버튼이 필요 없기 때문에 일정 스크롤 높이에서 부터 상단 이동 버튼이 등장했으면 좋을 것 같았다.

useState 를 활용해 버튼의 상태 값을 css에 적용했다.

showTopBtn ? "opacity-100 visible" : "opacity-0 invisible" showTopBtn가 true면 보이게 적용했다.

*visible 는 visibility: visible; / invisible 는 visibility: hidden; (tailwind 표기)

이제 필요한 거는 원하는 시점에서 showTopBtn가 true로 변하게 해야 됩니다.

  • useEffect 훅을 활용하여 컴포넌트가 마운트될 때 실행됩니다.
  • 스크롤 이벤트가 발생할 때마다 호출 되는 showBtnClick 함수가 정의합니다.
  • showBtnClick 함수 내에서 현재 스크롤 위치 (window.scrollY) 를 확인하고, 위치가 400보다 크면setShowTopBtn(true)를 호출하고 그렇지 않으면 setShowTopBtn(false) 를 호출합니다.
  • window.addEventListener("scroll", showBtnClick);를 통해 스크롤 이벤트에 대한 리스너가 등록되어 스크롤이 발생할 때마다 showBtnClick 함수가 호출됩니다.
  • 컴포넌트가 언마운트되면 return 블록 내의 함수가 실행되어 window.removeEventListener("scroll", showBtnClick); 를 통해 등록된 스크롤 이벤트 리스너를 제거하여 메모리 누수를 방지합니다.
  •  useEffect(() => {
        const showBtnClick = () => {
          if (window.scrollY > 400) {
            setShowTopBtn(true);
          } else {
            setShowTopBtn(false);
          }
        };
        window.addEventListener("scroll", showBtnClick);
        return () => {
          window.removeEventListener("scroll", showBtnClick);
        };
      }, []);

    전체 코드

    import { cls } from "libs/utils";
    import { useEffect, useState } from "react";
    import { HiChevronUp } from "react-icons/hi";
    
    export default function MoveToTop() {
      const [showTopBtn, setShowTopBtn] = useState(false);
      const moveToTop = () => {
        window.scrollTo({ top: 0, behavior: "smooth" });
      };
      useEffect(() => {
        const showBtnClick = () => {
          if (window.scrollY > 400) {
            setShowTopBtn(true);
          } else {
            setShowTopBtn(false);
          }
        };
        window.addEventListener("scroll", showBtnClick);
        return () => {
          window.removeEventListener("scroll", showBtnClick);
        };
      }, []);
      return (
        <div
          onClick={moveToTop}
          className={cls(
            showTopBtn ? "opacity-100 visible" : "opacity-0 invisible",
            "fixed bottom-7 right-4 lg:right-10 p-2 z-20 cursor-pointer hover:scale-90 transition-all duration-500 rounded-full bg-[#2c82f2]"
          )}
        >
          <HiChevronUp className="text-2xl text-white" />
        </div>
      );
    }

    추가 (2023.12.02)

    특정 영역 안에서 상단 이동

    다른 프로젝트에서 탭이 있는 모달 창을 만들던 중 스크롤이 생기는 모달이어서 탭 클릭 시 자동으로 위에서 부터 다시 시작하게 만들어야 했다.

    DOM요소에 접근하기 위해 사용되는 React Hook 인 useRef 를 활용하여 해당 영역을 참조한다.

    아래 코드에서는 참조된 영역의 scrollTop이 0이 아니면 수직 스크롤 바의 위치를 0으로 변경한다.

    import { useState, useEffect, useRef } from "react";
    
    export default function Ex() {
    	const [modalTab, setModalTab] = useState(1);
      const modalInfoModal = useRef<HTMLDivElement>(null);
    
      const selectModalTabHandler = (order: number) => {
        setModalTab(order);
    
        if (modalInfoModal.current.scrollTop !== 0) {  // 상단으로 이동
          modalInfoModal.current.scrollTop = 0;
        }
      };
    
    	return (
    		<Modal>
    			<ul>
             <li
               onClick={() => selectModalTabHandler(1)}
               className={modalTab === 1 ? "on" : ""}
             >
               탭 1
             </li>
             <li
               onClick={() => selectModalTabHandler(2)}
               className={modalTab === 2 ? "on" : ""}
              >
              탭 2
             </li>
          </ul>
    			<div
            ref={modalInfoModal}
           >
    				{modalTab === 1 ? (      
                <div className="topRank"> 탭 1 content</div>
            ) : (
                <div className="topRank"> 탭 2 content</div>
            )}
    			</div>
    		<Modal/>
    	)
    }