스크롤을 따라가는 특정div 만들기

Y b·2024년 3월 13일

서론


잡코리아 등 웹페이지를 보다보면 오른쪽에 스크롤을 따라 움직이는 div들, 즉 광고들이 보인다.

그 중에서 마켓컬리에서 결제금액이 결제 관련 (쿠폰, 포인트 등)div의 위치부터 움직이는 것이 보였다. 윗 부분에서는 움직이지 않는 div를 움직이도록 하는 것이다.

이를 구현해보고자 했고, 결제하기 페이지를 구성했다.

환경

next.js, 타입스크립트, tailwind, react-hook-useForm ,schad/cn 이용하고 있으며,
아래 코드를 변형한다면 react에도 적용가능함.

요약

  1. ref와 useEffect를 이용하여 스크롤 위치 확인
  2. 스타일 fixed => relative로 만들기

참고자료가 많이 없어 고민을 많이 했는데 결국 위의 내용처럼 성공하게 되었다.

과정

  1. div가 움직인다는 것에 집중하여, ref를 이용하여 스크롤을 이동하는 이벤트를 변형을 주려고 했다.
    이전에 페이지의 헤더를 누르면 특정 div로 이동하게끔 하는 페이지를 만들었다.
    그 코드를 가져와서 변형을 했다.

다만 이번에는 많은 ref가 필요하지 않았고, 상위 페이지의 제일 바깥 div의 ref도 필요없었다.
그때는 스크롤을 한번 내릴 때 하나씩 화면에 꽉찬 div가 나타났어야 했기 때문에 다른 조건이었다.

  1. 위의 논리대로 코드를 구성하면,
//useState를 이용하여 아래 jsx 코드 부분에서 논리 연산자(&&) 이용할 예정
const [IsTotalRef, setIsTotalRef] = useState(false);

//타입스크립트이기 때문에 타입지정
const totalDivRef = useRef<HTMLDivElement | null>(null);

  const onScroll = () => {
    if (totalDivRef.current) {
//ref가 null이 아닐 때, 특정 디브가 페이지 상단에 닿으면 IsTotalRef를 true로 변경
      const totalDivTop = totalDivRef.current.getBoundingClientRect().top;
      if (totalDivTop <= 0) {
        setIsTotalRef(true);
      } else {
        setIsTotalRef(false);
      }
    }
  };
  //useEffect를 통해 ref가 바뀔 때만 onScroll 함수 적용
  useEffect(() => {
    onScroll();
    window.addEventListener('scroll', onScroll);
    //아래 return 문이 스크롤 이벤트가 이용되지 않는 경우
    //cleanUp함수 역할을 해서 이벤트가 제거
    return () => {
      window.removeEventListener('scroll', onScroll);
    };
  }, [IsTotalRef]);
  

** getBoundingClientRect() 메서드는 DOMRect요소의 크기와 브라우저 뷰포트에 상대적인 위치에 대한 정보를 제공 하는 객체를 반환함. 위의 경우 dom의 top의 위치를 알려줌.
참고:
https://kyounghwan01.github.io/blog/JS/JSbasic/getBoundingClientRect/#%E1%84%80%E1%85%AE%E1%84%86%E1%85%AE%E1%86%AB

  1. 위의 과정을 마쳤는데 이걸 어떻게 따라가도록 구현할지 고민했다.
    예전에 div가 이동하는 것은 스크롤을 내릴 때마다 div가 움직이기 때문에 height를 움직였는데
    여기에선 위치를 계산하여 멈춰 있다가 움직여야 했다.

그래서 relative인 상태에서 fixed로 바뀌도록 했고, relative는 다른 상태로도 대체 가능하다.

  1. 3번의 논리대로 코드를 구성하면,
//바뀔 지점의 div에 ref 지정함
          <div ref={totalDivRef}>
              <div className="flex border-t-[2px] border-gray-200/15 pt-[50px]">
                <div className="grid justify-start justify-items-center w-[742px] pb-[300px]">
                  //...생략...
                    <Card
                      className={cn(
                  // 현재 next.js tailwind 스타일을 적용중
                 // IsTotalRef가 false인지 true인지에 따라 삼항연산자에 따라 스타일이 바뀐다.
                        IsTotalRef
                          ? 'fixed top-[20px] grid w-[300px] border-none'
                          : 'relative grid w-[300px] border-none',
                      )}>
                 //...생략...
                    </Card>
                  </div>
  1. 완성.

최종 결과

profile
웹 개발자

0개의 댓글