Intersection Observer 간단 정리하기

박성룡 ( Andrew park )
9 min readMar 6, 2020

최근 intersection observer 을 사용할 일이 생겨 해당 내용을 간단하게 정리해보고자 한다.

intersection observer 는 Target Element 가 화면에 노출되었는 지 여부를 간단하게 구독할 수 있는 API 이다.

만약 이를 구현한다고 생각해보면, scroll 이 일어 날때 마다, 특정 Element 가 화면 에 존재 여부를 계속 계산하는 code 를 만들어야 한다.

하지만 이는 개발자가 작성한 code 에 따라 성능이 좌우 될 수 있는데, intersection observer 를 쓰면, 간단하게 구현할 수 있을 뿐만아니라, 성능 상에도 매우 유리하다.

간단 예제

const options = { threshold: 1.0 };const callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
observer.unobserve(entry.target);
console.log('화면에서 노출됨');
} else {
console.log('화면에서 제외됨');
}
});
}
const observer = new IntersectionObserver(callback, options);observer.observe(
document.getElementById('id')
);

해당 과정을 살펴보면,

  1. Intersection Observer 객체를 생성하면서, Callback Functionoption 을 전달한다.
  2. Intersection Observer 에서 observe 로 구독할 Target Element 를 추가한다.
  3. Target Element 가 options.threshold 로 정의한 Percent(%) 만큼 화면에 노출 혹은 제외 되면, entries 배열 에 추가하고, Callback Function 을 호출한다.
  4. Callback Function 에서 전달 받은 entries 배열을 확인하면서, isIntersecting 으로 노출 여부를 확인한다.
  5. 만약 더이상 Target Element 를 구독할 필요가 없다면, IntersectionObserver 에서 unobserve 로 제거 할 수 있다.

Intersection Observer

Intersection Observer 를 생성할 때는 옵션을 설정할 수 있다.

옵션에는 root, rootMargin, threshold 가 있다.

1. root

root 로 정의된 Element 기준으로 Target Element 의 노출, 비노출 여부를 결정한다.

기본 값은 Browser ViewPort 이다.

만약 root 로 정의한 Element 의 Children 에 Target Element 가 존재하지 않는다면, 화면에 노출 되더라도, 노출로 보지 않는다.

만약 Target Element 가 Root Element 의 Children 이 되고, 화면에 노출되면, 노출로 보고 Callback Function 을 호출한다.

2. rootMargin

rootMargin 은 ‘0px 0px 0px 0px’ 형태로 정의할 수 있다.

rootMargin 이 있으면, threshold 계산할 때, rootMargin 영역 만큼 더 계산한다.

3. threshold

threshold 를 numberArray<number> 로 정의할 수 있다. 정의하지 않는다면 기본값은 0 이다.

number 로 정의할 경우, Target Element 의 노출 비율에 따라 한번 Callback Function 을 호출할 수 있지만, Array<number> 로 정의할 경우, 각각의 비율로 노출될 때마다 Callback Function 을 호출한다.

const optionOne = { threshold: 1.0 };
const observer = new IntersectionObserver(callBack, optionOne);
// isIntersecting: false, intersectionRatio: 0
// isIntersecting: true, intersectionRatio: 1
// isIntersecting: false, intersectionRatio: 0
const optionArray = { threshold: [0, 0.25, 0.5, 0.75, 1] };
const observer = new IntersectionObserver(callBack, options);
// isIntersecting: false, intersectionRatio: 0
// isIntersecting: true, intersectionRatio: 0.0216450225561857
// isIntersecting: true, intersectionRatio: 0.2532467544078827
// isIntersecting: true, intersectionRatio: 0.5043290257453918
// isIntersecting: true, intersectionRatio: 0.7510822415351868
// isIntersecting: true, intersectionRatio: 1
// isIntersecting: true, intersectionRatio: 0.7467532753944397
// isIntersecting: true, intersectionRatio: 0.49567100405693054
// isIntersecting: true, intersectionRatio: 0.24891774356365204
// isIntersecting: false, intersectionRatio: 0

Intersection Observer Entry

Target Element 의 노출 혹은 비노출 따라 Intersection Observer Entry 배열을 만들고, Callback Function 을 호출한다

이때 처음에는 모든 Element 의 노출, 비노출 여부를 체크하기 때문에 반드시 구독한 전체 Target Element 을 Entry Array 에 넣어 호출한다.

하나의 Intersection Observer Entry 에는 아래 속성값이 정의되어 있다.

  • target : Target Element
  • time : 노출되거나 비노출된 시간
  • isIntersecting : 노출 여부
  • intersectionRatio : 노출된 비율
  • intersectionRect : 노출된 영역
  • boundingClientRect : TargetElement.getBoundingClientRect() 값
  • rootBounds : RootElement의 bound 값 만약 옵션에서 지정하지 않았다면, 화면 크기 이다.

Image Lazy Load 구현해 보기

Intersection Observer 를 이용하면 쉽게 Image Lazy Load 를 구현할 수 있다.

이미지 리스트를 만들고, 이를 작은 이미지로 채운다.

그리고, 실제 노출되고 싶은 이미지를 Data 로 채워 넣는다.

// html
<img class=’lazy’ src=’empty.png’ data-src=’image1.png’ />
<img class=’lazy’ src=’empty.png’ data-src=’image2.png’ />

lazy className 으로 등록된 Element 를 IntersectionObserver 에 구독에 추가한다.

const options = { threshold: 0 };
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
observer.unobserve(entry.target);
entry.target.src = entry.target.dataset.src;
}
});
}
, options);
observer.observe(
Array.from(
document.getElementsByClassName('lazy')
)
);

노출이 될때마다, entry 의 target 을 가져와서 src 에 dataset.src 를 적용하고, 구독에서 제외한다.

이를 응용하면, 특정 Element 가 노출 될때마다, Animation 효과도 부여하거나, 사용자가 문서를 어디까지 읽었는지도 구현해 볼 수 있다.

Internet Explorer Polyfill 적용하기

Intersection Observer 는 IE 에서는 지원하지 않는다.

때문에 해당 기능을 사용하려면, Polyfill 라이브러리를 사용해야한다.

내부 코드를 살펴보면, 브라우저에 Intersection Observer 가 정의 되어 있다면 해당 내장 객체를 사용하고, 없다면,

IntersectionObserverEntryIntersectionObserverGlobalScope 에 정의한다.

IntersectionObserver 에서는 구독할 _observationTargets 배열 과 callback 에 전달할 _queuedEntries 배열 을 가지고 있으며,

observe 에 Target Element 가 전달되면, _observationTargets 배열에 push 한다.

_monitorIntersections 함수 로 resize 나 scroll 이벤트가 발생할 때마다, _checkForIntersections 함수 로 검사한다.

만약 브라우저에 MutationObserver 가 정의되어 있다면, callback 으로 _checkForIntersections 함수 를 전달하고, document 의 변경사항을 구독한다.

_checkForIntersections 함수 에서는 getBoundingClientRect 함수 로 Target 이 화면에 노출되었는지 확인하고, 노출되거나 제외 되었으면, IntersectionObserverEntry 를 생성해 _queuedEntries 배열에 push 한다.

마지막으로 callback 을 호출면서, _queuedEntries 배열를 전달한다.

해당 Polyfill 은 IE7 까지 사용할 수 있다.

--

--