본문 바로가기
React

React(77) 스크롤 시, 하위메뉴를 헤더 아래로 고정하기

by 새발개발JA 2022. 7. 12.
반응형

 

 

 

상단이 고정된 헤더가 있다.

스크롤 하다가 페이지 중간에서 하위메뉴를 만나면 헤더 바로 아래로 고정되는 기능을 구현하려고 한다.

구글링을 해보니 자바스크립트로 하위메뉴까지의 스크롤의 높이를 구한 뒤, 고정시키는 로직이 대다수였다.

고민고민하다가 CSS 만으로도 구현할 수 있다는 사실을 알아냈다.

많은 분들에게 도움이 될지는 모르겠지만 그래도 공유해보려고 한다.

 

결과화면 미리보기 

 

페이지 중간에 하위 메뉴 탭이 있다. 스크롤을 내려보자

 

스크롤을 내리다가 헤더가 하위탭을 만나자 고정되고, 그 아래의 내용들만 스크롤되기 시작한다.

 

 

 


 

 

React(77) 스크롤 시, 하위메뉴를 헤더 아래로 고정하기

 

 

Product.tsx

첫번째 useEffect 를 통해, 첫 렌더시 스크롤을 0으로 잡아주고, 

두번째 useEffect 를 통해, handleScroll 이라는 이벤트를 addEventListner로 실행해주자.

이때 리턴값으로 removeEventListner 해줘야 이벤트가 잘 마무으리된다. 

스크롤이벤트의 적용은 최상단 태그인 <div id="app"> 부터 시작하기 때문에

버블링보다는, 캡쳐링이 되야 제대로 적용이 되기 때문에 { capture : true } 옵션을 붙여주었다.

 useEffect(() => { 
   document.getElementById('app')?.scrollTo(0, 0); // 첫 렌더시 스크롤이 최상단 고정된다
  }, []);
  
  useEffect(() => {
    window.addEventListener('scroll', handleScroll, { capture: true }); // 스크롤 이벤트 등록
    return () => {
      window.removeEventListener('scroll', handleScroll); // 스크롤 이벤트 등록 제거(성능저하방지)
    };
  }, []);

 

 

 

함수 handleScroll 에서는,

 scrollTop 현재 스크롤 위치가  >= 하위메뉴바 위치보다 더 클때, (즉 아래일 때)

하위 탭 메뉴의 <div className=""> 에 useRef를 통해 css sticky 속성를 추가해주었다.

const handleScroll = useCallback(() => {
    if (!tabRef.current || !detailRef.current || !document.getElementById('app'))
      return;
    }

    // 스크롤의 실시간 위치
    const scrollTop: number | undefined =
      document.getElementById('app')?.scrollTop; // 최상단 div 기준으로 스크롤 위치를 감지
      
      // 스크롤 위치가 tabRef(하위메뉴 탭)의 위치보다 아래이면
      if (scrollTop >= tabRef.current.offsetTop) {
        fixTab.current = true;   // fixTab 변수는 트루
      } else {		         // 그렇지 않으면
        fixTab.current = false;  // fixTab 변수는 false
      }

      // 스크롤 위치가 detailRef(하위메뉴 2번)의 위치보다 위이면
      if (scrollTop < detailRef.current.offsetTop - offset) {
        setTab(0); // 하위메뉴 탭은 자동으로 인덱스 0을 보여주자
      } 
      // 스크롤 위치가 detailRef(하위메뉴 2번)의 위치이거나 아래이면
      else if (scrollTop >= detailRef.current.offsetTop - offset) {
        setTab(1); // 하위메뉴 탭은 자동으로 인덱스 0을 보여주자
      } 

    }
  }, [tabRef.current, detailRef.current]);

 

 

하위메뉴 탭 영역에 fixTab 변수를 넣어 스크롤을 내렸을 때, css 를 변경해줄 준비를 해주자

 <div className="product">
    <div>
      아무 내용 (타이틀/사진 등의 정보가 들어감)
    </div>
    
    <div 
       className={`tabs ${fixTab ? 'fixed' : ''}`} // 얘를 변경 해서 메뉴바를 붙여줌 
       ref={tabRef}
    >
      <div className={`${tab === 0 ? 'bold-line' : ''}`}> // 선택한 탭# 이면 밑줄쫙
        하위메뉴 탭1
      </div>
      <div className={`${tab === 1 ? 'bold-line' : ''}`}>
        하위메뉴 탭2
      </div>
    </div>
    
    <div className="content">
      <div> 
        하위메뉴 탭1 내용 
      </div>
      <div ref={detailRef}> 
        하위메뉴 탭2 내용        
      </div>
    </div>
</div>

 

 

fixTab이 true 일때 fixed 가 클래스이름에 추가되고, css 로 고정시켜주자

.tabs .fixed {
  top: 40px; 	    // 헤더 높이만큼 내려와서 
  position: sticky; // sticky로 고정시켜주자
  z-index: 5;	
}

 

 

 

 

 

반년전에 정리해놓은 포스팅이다. 코드 정리해야지해야지 하면서 미루다가 이제서야 내보낸다.

그 당시에는 이해도 잘안가고, 무작정 찾다가 헤매기만 했는데 지금 코드를 보니 감회가 새롭다.

그동안 성장하는 걸까 잘하고 있는걸까 의구심만 들었는데, 그래도 꾸준히 걸어가고 있었구나를 다시한번 느끼게 된다.

기술은 빠르게 급변하고 심리적으로 조급하게 느껴지는 순간도 있지만, 나만의 속도로 꾸준히 걸어가보려고 한다. 

멈추지만 않으면 계속 성장하고 있는 것이다.

 

 

 

 

반응형

댓글