리액트 액티브 탭 구현하기

백우진·2023년 9월 24일
0
post-custom-banner

스크롤을 내리며 원하는 항목에 지날때 해당되는 탭 요소가 액티브 되어 보여주는 효과를 구현하게 되었고 구현 방법을 소개하고자 한다


구현 원리

  1. 각 주요 Section 별 Div태그에 동일한 className을 지정
  2. 스크롤 이벤트를 추가하여 해당 Section 의 className이 몇번째에 나온 것인지 판단하여 인덱스 값을 판단
  3. 해당 방법으로 현재 어디 지점을 지나고 있는지 파악 가능



구현 코드

  1. 다섯개의 Div를 임의로 생성하고 동일한 className scroll-tab을 지정

  2. 사용자는 스크롤을 돌리게 되면 스크롤 이벤트가 발생하고 handleScroll함수가 동작하게 된다.

  3. handleScroll함수의 동작은 다음과 같다.
    3-1) export const handleScroll = (setValue: Function) => (event: Event) => { ... }
    handleScroll 함수는 함수를 반환하는 함수입니다. 이는 함수 커링(function currying) 기법을 사용하는 것이며, setValue라는 함수를 인자로 받습니다 이렇게 하면 setValue 함수를 내부 함수에서 사용할 수 있게 됩니다.

    3-2) const divElements = document.querySelectorAll('.scroll-tab')
    document.querySelectorAll('.scroll-tab')을 사용하여 CSS 클래스 이름이 "scroll-tab"인 모든 요소를 선택합니다. 이 요소들은 페이지의 각 섹션을 나타냅니다.

    3-3) const scrollY = window.scrollY
    window.scrollY를 사용하여 현재 페이지의 수직 스크롤 위치를 가져옵니다. 이 값은 페이지의 맨 위에서부터 스크롤한 거리를 나타냅니다.

    3-4) for (let i = 0; i < divElements.length; i++) { ... }
    선택된 .scroll-tab 요소들을 반복하여 각 섹션의 위치 정보를 확인합니다.

    3-5) const { top, bottom } = divElement.getBoundingClientRect()
    getBoundingClientRect() 메서드를 사용하여 현재 반복 중인 섹션의 상단(top)과 하단(bottom)의 위치 정보를 가져옵니다. 이 정보는 현재 화면에 보이는 섹션의 위치를 나타냅니다.

    3-6) if (top <= window.innerHeight / 2 && bottom >= window.innerHeight / 2) { ... }
    현재 섹션의 상단이 화면 중앙 이하에 있고, 하단이 화면 중앙 이상에 있을 때, 해당 섹션은 화면 중앙에 표시되는 것으로 간주합니다. 이러한 조건을 만족하면 해당 섹션을 활성 상태로 처리합니다.

    3-7) setValue(i)
    활성 섹션의 인덱스 i를 setValue 함수를 통해 설정합니다. 이렇게 하면 현재 화면 중앙에 있는 섹션의 인덱스가 저장되어, 컴포넌트 내에서 이를 사용하여 활성 탭을 표시하거나 다른 작업을 수행할 수 있습니다.

  1. 컴포넌트의 AcriveTab은 현재 몇번째 Scroll-tab을 지나고 있는지 상태 값을 저장하게 되고 사용자는 해당 Tab에서 조건부 랜더링을 통해서 Activeg한 효과를 구현 할 수 있다.


const Components = () => {
  const [activeTab, setActiveTab] = useState<number>(0);

  useEffect(() => {
    const scrollEventListener = handleScroll(setActiveTab);
    window.addEventListener('scroll', scrollEventListener);
    return () => {
      window.removeEventListener('scroll', scrollEventListener);
    };
  }, []);

  return (
    <>
      <div className="scroll-tab">첫번째 섹션</div>
      <div className="scroll-tab">두번째 섹션</div>
      <div className="scroll-tab">세번째 섹션</div>
      <div className="scroll-tab">네번째 섹션</div>
      <div className="scroll-tab">다섯번째 섹션</div>
    </>
  );
};

export default Components;

export const handleScroll = (setValue: Function) => (event: Event) => {
  const divElements = document.querySelectorAll('.scroll-tab');

  // 현재 스크롤 위치를 가져옵니다.
  const scrollY = window.scrollY;

  // 각 div 요소의 위치 정보를 확인하고, 현재 스크롤 위치에 따라 활성 div를 결정합니다.
  for (let i = 0; i < divElements.length; i++) {
    const divElement = divElements[i];
    const { top, bottom } = divElement.getBoundingClientRect();

    // 예를 들어, 상단과 하단이 화면 중앙 이내에 있을 때 해당 div를 활성으로 표시합니다.
    if (top <= window.innerHeight / 2 && bottom >= window.innerHeight / 2) {
      setValue(i); // 활성 div의 인덱스를 상태에 저장합니다.
      break;
    }
  }
};

마무리

이렇게 구현 하는 방법보다 더 효율적인 방법이 있을 수 있으며 틀린 부분이 있을 수 있어, 더 좋은 방법이나 수정해야할 부분이 있다면 댓글로 남겨주세요!

profile
안녕하세요.
post-custom-banner

0개의 댓글