target과 root의 교차 발생을 비동기적으로 관찰하는 Web API이다. 메인 thread에 영향을 주지 않고, callback을 실행할 수 있도록 한다. 매번 layout을 새로 그려 render tree를 새로 만들지 않고 callback을 실행하므로 브라우저의 성능을 향상시킬 수 있다.
우리가 스크롤을 사용하면 scroll event가 발생하게 된다.
그러므로 우리는 호출 수 제한하는 방법 debounce와 throttle을 사용했었다.
하지만 intersection Observer를 사용함으로써 이 부분을 줄여준다.
또한 스크롤 이벤트에서는 현재의 높이값을 알기 위해 offsetTop
을 사용한다. 이 때 정확한 값을 가져오기 위해 매번 layout을 새로 그리게 된다. 하지만 intersection observer를 이용하면 repaint를 줄일 수 있기에 성능상 아주 좋은 api이다.
이제 사용법과 해당 메소드, 옵션 등에 대해서 알아보자!
일단 Intersection Observer
인스턴스를 생성해보면 다음과 같다.
let options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
// options에 따라 인스턴스 생성
let observer = new IntersectionObserver(callback, options);
// 타겟 요소 관찰 시작
let target = document.querySelector('#listItem');
observer.observe(target);
new
키워드를 통해 인스턴스를 생성한다. callback
, options
2개의 파라미터를 받고, callback
은 가시성의 변화가 생겼을 때 호출되는 콜백 로직이다. options
는 만들어진 인스턴스에서 콜백이 호출되는 상황을 정의한다.
new IntersectionObserver(callback(entries, observer), [options]);
관찰이 시작되는 상황에서 옵션을 설정할 수 있다. 기본 값이 정해져있기에 필수는 아니다!
어떤 요소를 기준으로 target이 들어오고 나가는 것을 확인하는지 지정
기본값을 null, 브라우저 ViewPort이다.
root: null -> ViewPort
root: document.querySelector('.counter') -> root범위를 .countainer로 지정
정리) 교차 기준이 되는 엘리먼트 observer의 상위 엘리먼트 여야한다.
default: null
root의 범위를 확장하거나 축소할 수 있다.
CSS
의 margin
과 유사하게 top
, right
, bottom
, left
의 margin
정도를 각각 설정할 수 있다.
기본 값은 0이며, 따로 설정 시 단위를 꼭 입력해야한다.
기본값은 '0px, 0px, 0px, 0px'
정리) rootMargin: root로 지정된 엘리먼트의 margin 값 설정
default: 상하좌우 모두 0px
target과 root의 교차가 얼마나 일어나야 callback이 호출될지 표시하는 것이다. 이 때 단일 숫자나 숫자 배열이 들어갈 수 있다. 즉, 요소의 top
, bottom
이 노출된 순간만 콜백을 실행할 수 있는 것이 아니라 어느정도 타겟 요소가 보여졌는지에 따라 콜백을 호출할 수도 있다. 예를 들어 요소가 50%만큼 보여졌을 때 탐지하고 싶다면 단일 숫자 값 0.5
를 설정하면 된다. 혹은 25%단위로 가시성이 변경될 때마다 콜백이 실행되게 하고 싶다면 [0, 0.25, 0.5, 0.75, 1]
을 설정하면 된다.
또한 0.0(target이 root영역에 진입시작)이며 1.0(target전체가 root와 교차)사이를 숫자로 표시한다. 이 때 기본 값은 0이다.
// 타겟 요소가 50% 가시성이 확인되었을 때
let observer1 = new IntersectionObserver(callback, {
threshold: 0.5
});
// 타겟 요소가 25% 단위로 가시성이 확인되었을 때
let observer1 = new IntersectionObserver(callback, {
threshold: [0, 0.25, 0.5, 0.75, 1]
});
정리) thredhold: root 엘리먼트와 observer 엘리먼트가 얼만큼 교차되었는지
0은 전혀 교차되지 않음, 1은 전체가 교차됨을 의미한다
default: 0
타겟 요소의 관찰이 시작되거나, 가시성에 변화가 감지되면(threshold
와 만나면) 등록된 callback
이 실행된다.
즉, target과 root가 교차되어 화면에 보이게 되었을 때 호출되는 함수이다. callback은 entries와 observer를 매개변수로 받는다.
let callback = (entries, observer) => {
entries.forEach(entry => {
// 각 entry는 가시성 변화가 감지될 때마다 발생하고 그 context를 나타냅니다.
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
이 콜백은 메인스레드에서 처리되고 파라미터로 entries
와 observer
를 받게 된다.
IntersectionObserverEntry 객체 리스트를 배열 형식으로 반환
boundingClientRect
intersectionRatio
intersectionRect
isIntersecting
rootBounds
target
콜백이 호출되는 Intersection Observer
IntersectionObserver.observe(targetElement)
: 타겟 요소에 대한 관찰을 시작한다.(관찰 시작)IntersectionObserver.unobserve(targetElement)
: 타겟 요소에 대한 관찰을 중지한다. 관찰의 목적이 이루어져 굳이 계속 관찰을 할 필요가 없는 경우 사용한다.(관찰 종료)IntersectionObserver.disconnect()
: 인스턴스의 타겟 요소들에 대한 모든 관찰을 중지한다.(관찰 멈추기)IntersectionObserver.takerecords(targetElement)
: IntersectionObserverEntry
인스턴스들의 배열을 리턴한다.이제 위를 바탕으로 사용할 코드를 정리해서 설명해보자면 다음과 같다.
우선 ref
값이 변할 때 새로운 옵저버를 등록해야한다.
useEffect(() => {
let observer;
if (ref) {
observer = new IntersectionObserver(checkIntersect, {
threshold: 0.4
});
observer.observe(ref);
}
return () => observer && observer.disconnect();
}, [ref]);
이제 target과 root가 교차되어 화면에 보이게 되었을 때 호출되는 함수인 callback을 실행하기 위해서 아래와 같이 작성해주어야한다.
const checkIntersect = useCallback(async ([entry], observer) => {
// entry.isIntersection: target과 root가 교차된 상태인지 boolean으로 반환
if (entry.isIntersecting && !isLoaded) {
// observer.unobserve: 관찰 중지
observer.unobserve(entry.target);
await getMoreItem();
// observer.observe: 관찰 시작
observer.observe(entry.target);
}
}, []);
이제 setRef를 넘겨주어서 ref를 변경시킬 수 있도록 한다.
return [ref, setRef];
적용한 최종 코드는 다음과 같다.
출처) Intersection Observer로 무한 스크롤 구현하기
실무에서 느낀 점을 곁들인 Intersection Observer API 정리
예제 참고)
[JavaScipt] Intersection Observer 정리
React 무한 스크롤 구현하기 with Intersection Observer
React - Intersection Observer API를 사용하여 인피니트 스크롤 구현하기
vue - Intersection Observer
infinite-scroll