Intersection Observer 간단 정리하기
최근 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')
);
해당 과정을 살펴보면,
Intersection Observer
객체를 생성하면서,Callback Function
과option
을 전달한다.Intersection Observer
에서 observe 로 구독할Target Element
를 추가한다.Target Element
가 options.threshold 로 정의한 Percent(%) 만큼 화면에 노출 혹은 제외 되면, entries 배열 에 추가하고,Callback Function
을 호출한다.Callback Function
에서 전달 받은 entries 배열을 확인하면서, isIntersecting 으로 노출 여부를 확인한다.- 만약 더이상
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 를 number
나 Array<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: 0const 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 Elementtime
: 노출되거나 비노출된 시간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 가 정의 되어 있다면 해당 내장 객체를 사용하고, 없다면,
IntersectionObserverEntry 와 IntersectionObserver 를 GlobalScope 에 정의한다.
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 까지 사용할 수 있다.