Intersection Observer API

pyozz·2023년 12월 29일
0
post-thumbnail

Intersection Observer API란?

브라우저에서 제공하는 API 중 하나로, 요소가 뷰포트에 나타날 때를 관찰하는 API

따라서 나타나는 그 시점에 특정 코드를 수행하게 할 수 있다.

무한 스크롤 (Infinite Scroll)

무한 스크롤은 사용자가 페이지 하단에 도달했을 때마다 요소들을 추가로 로드해서 마치 무한히 스크롤 할 수 있다는 사용자 경험을 주는 기능이다. 이것을 Intersection Observer API로 구현할 수 있다.

예제 1

먼저 무한 스크롤을 보기 전에 이해를 돕기 위한 첫 번째 예시로 스크롤 가능한 페이지 하단에 원이 있다고 할 때
Intersection Observer API를 사용하면 사용자에게 원이 표시되는 시점을 감지해서 특정 코드를 수행할 수 있다.

<body>
  <div id="circle"></div>
</body>
// 1. 감지할 요소를 취득하고
const $circle = document.getElementById('circle');

// 2. IntersectionObserver 생성자 함수 호출
const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        console.log(entry.target, 'visible !!');
      }
    });
  },
  {
    // 옵션 객체
    // root: null,
    // rootMargin: '0px', 
    // threshold: 1.0 
  }
);

// 3. observer에게 요소가 감지되면 콜백함수를 실행하라고 지시한다.
observer.observe($circle);

다음과 같이 콘솔에 결과가 잘 나오는 것을 확인할 수 있다.

위 2번 코드를 자세히 살펴보면...
IntersectionObserver 생성자 함수를 호출하는데 이때 요소를 감지했을 때 실행할 콜백 함수를 필수로 넣어주어야 한다. 두 번째 인수로 옵션 객체를 넣어주는데 이는 선택사항이다.

참고로 옵션 객체에는 다음과 같은 속성들이 있다.

  • root: 관찰 대상을 감지하는 영역으로 기본값은 null(viewport)이다.
  • rootMargin: 루트 영역에 대한 여백 설정으로 기본값은 '0px'이다.
  • threshold: 관찰 대상이 감지 영역에 얼마나 들어왔을 때 콜백 함수를 실행할지를 결정하는 속성으로 기본값은 0이다.
{
 root: null, // 뷰포트를 기준으로 설정
 rootMargin: '0px', // 루트 영역에 대한 여백 설정
 threshold: 0.5 // 요소의 50%가 뷰포트 안에 들어올 때 콜백함수 실행
};

isIntersecting 속성으로 배열의 각 요소들이 화면에 나타나는지 확인해서 특정 코드를 수행하게 한다.

예제2

이제 본격적으로 무한 스크롤을 구현해 볼건데 원리는 간단하다.

  1. 먼저 전체 개수가 아닌 그 중 n개의 데이터만 가져온 뒤 각각에 대해서 요소를 생성하는데 마지막에 관찰 대상이 될 요소를 따로 추가하고
  2. Intersection Observer API로 그 대상을 관찰한 뒤 그 요소가 화면에 보이게 되면 서버에 다음 페이지에 대한 데이터를 요청하여 렌더링하는 것이다.

이때 주의할 점은 한번 감지가 된 요소는 unobserve 메서드로 관찰을 취소하여 함수를 실행시키지 않는 것이다.

<div id="postsContainer"></div>
(function () {
  'use strict';

  const $postsContainer = document.getElementById('postsContainer');
  let currentPage = 1;
  const limit = 10;

  const setScrollObserver = ($scrollObserverElement) => {
    const callback = async (entries) => {
      for (const entry of entries) {
        if (entry.isIntersecting) {
          const postList = await getPosts(++currentPage);
          if (postList.length > 0) {
            renderPostList(postList);
          }

          // 요소가 관찰된 이후 요소 제거 및 관찰을 취소하지 않으면 
          // 그 요소가 isIntersecting될 때마다 콜백함수가 실행되는 문제가 발생한다.
          observer.unobserve(entry.target);
          entry.target.remove();
        }
      }
    };

    // 3. 인스턴스를 생성하고 전달한 요소가 관찰되면 콜백함수를 실행시키라고 지시한다.
    const observer = new IntersectionObserver(callback);
    observer.observe($scrollObserverElement);
  };

  const createObserverElement = () => {
    // div 요소 생성 후 반환
  };

  const renderPostList = (postList) => {
    // forEach
    // innerHTML

    // 2. 추가된 요소 마지막에 무한 스크롤 현상을 일으킬 관찰될 요소를 추가한다.
    const $observerElement = createObserverElement();
    $postsContainer.appendChild($observerElement);
    
    setScrollObserver($observerElement);
  };

  const getPosts = async (currentPage) => {
    // fetch
  };

  // 1. init 함수가 실행되면 1번 페이지의 아이템을 요청하고 렌더링한다.
  const init = async () => {
    const postList = await getPosts(currentPage);
    renderPostList(postList);
  };
  init();
})();

0개의 댓글

관련 채용 정보