IntersectionObserver

세니·2025년 1월 10일
0

무한 스크롤링에 대해 평소에 궁금했는데 Web API에서 제공하는 IntersectionObserver에 대해 처음 알게되어 글을 쓰는 계기가 되었다.

IntersectionObserver API?

IntersectionObserver API는 상위 요소 또는 최상위 문서의 viewport와 대상 요소 사이의 변화를 비동기적으로 관찰할 수 있는 수단으로 주로 아래 네가지의 목적에 맞게 사용이 된다.

  • 페이지가 스크롤 될 때 이미지 또는 다른 컨텐츠의 지연 로딩이 된다.
  • 스크롤할 때 더 많은 컨텐츠가 로드되고 렌더링 되는 무한 스크롤 웹 사이트를 구현함으로 써 사용자가 페이지를 넘길 필요가 없다.
  • 광고 수익 산정을 위해 광고 가시성을 보고한다.
  • 사용자가 결과를 볼 수 있을지 여부에 따라 작업 또는 애니메이션 프로세스를 수행할지 여부를 결정한다.

이 API는 특정 요소가 viewport와의 교차점에 들어가거나 나갈 때 또는 두 요소 간의 교차점이 지정된 양만큼 변화될 때 실행되는 콜백 함수를 등록할 수 있다.

관찰 대상 요소는 기기의 viewport 또는 특정 요소와 교차한다. Intersection Observer API에서는 이 viewport나 특정 요소를 root 요소라고 한다.

viewport와 관련된 교차를 관찰(observe)하기 위해서는 rootnull 로 지정해야 한다.

Intersection Observer API는 IntersectionObsever 객체를 선언하면서 사용이 된다.

let options = {
	root: null,
	rootMargin: "0px",
	threshold: 1.0,
}
let observer = new IntersectionObserver((entries) => {}, options); // callback, options

Options

options는 observer의 콜백이 언제 호출되는지 제어하는 요소이다.

  • root : observe하기 위한 타겟의 상위 요소, 지정하지 않거나 null이면 브라우저 뷰포트가 기본으로 설정된다.
  • rootMargin : root의 주위 여백으로 css의 margin속성과 비슷한 값을 가진다. 기본 값은 0이다.
  • threshold : observe의 콜백이 실행되어야 하는 대상의 가시성 백분율을 나타내는 숫자 또는 숫자 배열

callback

IntersectionObserver의 callback은 IntersectionObserverEntry 객체와 observe 목록을 받는다.

let callback = (entries, observer) => {
  entries.forEach((entry) => {
    // 각 엔트리는 관찰된 하나의 교차 변화을 설명합니다.
    // 대상 요소:
    //   entry.boundingClientRect
    //   entry.intersectionRatio
    //   entry.intersectionRect
    //   entry.isIntersecting
    //   entry.rootBounds
    //   entry.target
    //   entry.time
    if (entry.isIntersection){
	    console.log("교차되었다.")
    } else {
	    console.log("교차되지 않았다.")
    }
  });
};

만일 entry가 현재 root와 교차하는지 보기 위해서는 isIntersection 을 확인 하면 된다. 이 값은 boolean으로 교차된다면 true를 아니면 false를 나타낸다.

Method

  • IntersectionObserver.observe() : 주어진 target을 observer에 추가한다.
  • IntersectionObserver.unobserve() : target을 observe 하는것을 중단한다.
  • IntersectionObserver.disconnect() : target을 observe하는 것을 해제한다.
  • IntersectionObserver.takeRecords() : target에서 마지막 교차 변화 알림 후 호출하고 교차 영역의 변화를 감지한 대상 요소를 리턴한다.

어떤 개념인지와 속성, 메서드들을 확인해봤는데 어떻게 사용해야 할지 아직 감이 오지 않는다. 예시를 통해서 한번 이해를 해봐야 하므로 React.js로 한 div 박스의 변화를 봐야할 것 같다.

Example

뷰포트 기준 div 요소들이 있는데 만일 요소가 80% 보인다면 css opacity 값을 설정하도록 했다.

React.js 기준으로 작성해 Observer를 useRef로 관리하고 div 리스트들을 ref로 하나씩 observe하도록 설정한 부분이 핵심인듯하다.

// App.js
import { useRef } from "react"
import "./App.css"

const list = ["box1", "box2", "box3", "box4", "box5", "box6", "box7", "box8"];
function App() {
  let observerRef = useRef(null);

  observerRef.current = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
        if (entry.isIntersecting) {
          entry.target.classList.remove("hide")
        } else {
          entry.target.classList.add("hide")
        }
      })
    }, {
      root: null,
      threshold: 0.8
  })

  const handleObserve = (ref) => {
    if (ref && observerRef.current) {
      observerRef.current.observe(ref)
    }
  }
  return (
    <>
    <div className="container">
      {
        list.map((box) => (
          <div className="target hide" ref={(ref) => handleObserve(ref)} data-target={box} key={box}>{box}</div>
        ))
      }
    </div>
    </>
  );
}

export default App;
// App.css
.container {
  position: relative;
  top: 800px;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.target {
  width: 100px;
  height: 100px;
  background-color:lightblue;
  margin-top: 10px;
}

.hide {
  opacity: 0.5;
}

나는 스크롤을 발생하기 위해서 컨테이너 박스를 일부러 위에서 800px 만큼 떨어트려 동작을 확인하도록 했다.

이렇게 스크롤 하기 전에는 hide class가 있는 반면

일정 스크롤을 내리면 box1은 hide class가 제거되어 opacity 속성이 없어 제대로 보이고 box2는 아직 요소가 80% 보이지 않아 hide class가 남아 있는 것을 볼 수 있다.

출처

IntersectionObserver에 개념만 알고 무지했는데 확실히 예제를 한번 사용해보니 어느정도 감이 잡히는것같다… (MDN이 진짜 잘되어있네..)

profile
세니는 무엇을 하고 있을까

0개의 댓글