본문 바로가기

웹/JavaScript

[JavaScript] Intersection Observer API - 화면 내 보이는 요소 검사

개요

직역하면 교차 지점 관찰자 API.

간단히 말하자면, 페이지 안에서 사용자 눈에 요소가 보이는지, 혹은 페이지 바깥에 있어서 보이지 않는지와 관련된 API라 할 수 있다.

어떨 때 필요할까?

MDN docs에서는 이 API가 나오게 된 주요한 이유를 이렇게 설명하고 있다.

  • 페이지가 스크롤 되는 도중에 발생하는 이미지나 다른 컨텐츠의 지연 로딩(lazy-loading)
  • 스크롤 시에, 더 많은 컨텐츠가 로드 및 렌더링되어 사용자가 페이지를 이동하지 않아도 되게 하는 무한 스크롤(infinite-scroll)을 구현
  • 광고 수익을 계산하기 위한 용도로 광고의 가시성 보고
  • 사용자에게 결과가 표시되는 여부에 따라 작업이나 애니메이션을 수행할 지 여부를 결정

좀 더 구체적으로 예시를 들어 보자.

만약 스크롤을 내려야 나타나는 div 요소가 움직이는걸 보여주고 싶다고 하자. 애니메이션을 사용자에게 보여주는 게 목적이니, 계속 애니메이션을 실행하고 있기보단 이 API로 검사를 해 div가 사용자에게 보이는 영역 안에 들어왔을 때 실행시키면 의도한 대로의 명확한 타이밍에 애니메이션을 보여줄 수 있을 것이다.

무한 스크롤로 리스트를 계속해서 생성해야 한다고 할 때, 현재 보여주고 있는 리스트의 마지막 요소가 보여질 때를 감지해 그 때 새로운 리스트를 추가할 수 있다. getBoundingClientRect를 사용할 수도 있겠지만, 스크롤을 할 때마다 콜백이 실행되어 성능이 좋지 않다.

 

사용 방법

1. 인스턴스 생성, 교차 관찰 옵션 설정

//옵션 설정
let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}

//인스턴스 생성
let observer = new IntersectionObserver(callback, options);

- observer의 매개변수 중 callback에, 타겟 요소가 화면 안에 들어왔을 때 어떤 동작을 할 지 작성한다.

- IntersectionObserver 인스턴스를 생성한다. (옵션은 생략 가능하며, 이 경우 기본값이 설정된다.)

- options에서 설정할 수 있는 요소는 다음과 같다.

  1. root: 사용자 눈에 보이는 영역이 무엇이 되는지 설정한다. 당연하지만 타겟 요소보다 상위 요소여야 한다. (기본값: 브라우저 뷰포트)
  2. rootMargin: root 로 설정한 요소가 가진 여백. 화면의 범위를 여백만큼 늘리거나 줄일 수 있다. px과 같은 단위를 입력해야 한다. (기본값: 0px)
  3. thereshold: 요소의 몇 % 이상이 화면에 보여졌을 때 콜백을 실행할지를 설정한다. 0.0 ~ 1.0 의 숫자로 퍼센트를 나타낸다. (기본값: 0)

 

2. 관찰 요소 타겟팅

//타겟팅
let target = document.querySelector('#listItem');
observer.observe(target);

target으로 설정한 요소가 관찰 대상이 되어 뷰포트 안에 들어왔는지 여부에 따라 observer의 콜백함수가 실행된다.

 

주요 메서드 (IntersectionObserver)

observe(targetElement) - 타겟 요소를 주시

unobserve(targetElement) - 타겟 요소에 대한 주시를 해제

disconnect() - 모든 대상의 주시를 해제

 

3. 콜백 함수

let callback = (entries, observer) => {
// 지정된 임계값을 충족할 때마다 콜백이 호출됨
  entries.forEach(entry => { 
    // 타겟 요소:
    //   entry.boundingClientRect
    //   entry.intersectionRatio
    //   entry.intersectionRect
    //   entry.isIntersecting
    //   entry.rootBounds
    //   entry.target
    //   entry.time
  });
};

엔트리 속성 (IntersectionObserverEntry)

boundingClientRect - 관찰 대상 요소를 둘러싸는 최소크기의 경계 사각형

intersectionRect - 교차 루트 내에서 관찰 대상 요소의 볼 수 있는 부분을 포함하는 가장 작은 사각형

intersectionRatio - 루트의 교차 비율 내에서 현재 볼 수 있는 대상 요소의 비율 (0.0 ~1.0)

isintersecting - 상 요소가 교차 관찰자의 루트와 교차하는지 여부.

rootBounds - 지정 루트 요소의 사각형 정보

target - 관찰 대상이 되는 요소

time - 교차 변경이 일어난 시간

 

예시 코드

다음은 마지막 요소를 감지하면 추가 리스트를 만들어주는 무한 스크롤 예시 코드이다.

const boxes = document.querySelector(".boxes");
let index = 0;

let options = {
  root: null, //기본값인 뷰포트로 설정
  rootMargin: "0px", //기준 margin 0px
  threshold: 0.25 // 콜백 호출 기준은 요소의 25% 만큼 보일 때
};

let io = new IntersectionObserver(callbackFunction, options);

//콜백함수
function callbackFunction(entries, observer) {
  entries.forEach((entry) => {
    //교차가 감지되면
    if (entry.isIntersecting) {
    //리스트를 한번에 10개씩 생성
      for (let i = 0; i < index + 10; i++) {
        const item = document.createElement("div");
        item.className = "box";
        item.innerHTML = `<p class="index">list${index + i}</p>`;
        boxes.appendChild(item);
      }
      index += 10; 
    }
  });
}

//리스트 마지막 요소를 관찰하게 함
io.observe(document.querySelector(".list-end"));

실행

 

주의사항

사파리와 파이어폭스 안드로이드 브라우저에서는 일부 기능이 동작하지 않을 수 있다.


참고 자료

MDN Docs

Easily implement Infinite Scrolling using Intersection Observer in vanilla JavaScript