IntersectionObserver는 WebAPI로 비동기적으로 실행되며 관찰 대상과 뷰포트의 교차점을 관찰하고 뷰포트 안으로 들어오는 시점에 정보를 제공하는 기능을 한다.
아래와 같은 상황에 활용한다.
Intersection observer를 생성하기 위해서는 생성자 호출 시 콜백 함수를 제공해야 한다. 이 콜백은 함수 threshold가 한 반향 혹은 다른 방향으로 교차할 때 실행된다.
let options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
let observer = new IntersectionObserver(callback, options);
let callback = (entries, observer) => {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
let target = document.querySelector('#listItem');
observer.observe(target);
IntersectionObserver를 이용하면 동적 이미지 로딩을 손쉽게 구현 할 수 있다. 구현 방법은 대상 element가 뷰포트에 들어왔을 때 이미지 경로를 변경 한 뒤 unobserve시킨다. 다음의 코드는 간단한 예제 코드이다.
<div class="example">
<img src="https://picsum.photos/600/400/?random?0" alt="random image" class="image-default">
<img data-src="https://picsum.photos/600/400/?random?1" alt="random image" class="image">
<img data-src="https://picsum.photos/600/400/?random?2" alt="random image" class="image">
<img data-src="https://picsum.photos/600/400/?random?3" alt="random image" class="image">
<img data-src="https://picsum.photos/600/400/?random?4" alt="random image" class="image">
<img data-src="https://picsum.photos/600/400/?random?5" alt="random image" class="image">
<img data-src="https://picsum.photos/600/400/?random?6" alt="random image" class="image">
<img data-src="https://picsum.photos/600/400/?random?7" alt="random image" class="image">
</div>
동적 이미지 로딩을 수행할 이미지 태그의 경우 dataset 속성에 이미지 url을 담아둔다.
그 후 IntersectionObserver API를 활용하여 관찰하는 뷰포트 안으로 들어왔을 경우 dataset 속성에 이미지 url을 src 속성으로 할당해준다. 주의해야 할 점은 사용자에게 바로 노출되는 최상위 이미지의 경우 동적 이미지 로딩이 필요하지 않으므로 일반적인 방법과 같이 src 속성에 이미지 url을 담아둔다.
// IntersectionObserver의 options를 설정
const options = {
root: null,
// 타겟 이미지 접근 전 이미지를 불러오기 위해 rootMargin을 설정
rootMargin: '0px 0px 30px 0px',
threshold: 0
}
// IntersectionObserver 를 등록
const io = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
// 관찰 대상이 viewport 안에 들어온 경우 image 로드
if (entry.isIntersecting) {
// data-src 정보를 타켓의 src 속성에 설정
entry.target.src = entry.target.dataset.src;
// 이미지를 불러왔다면 타켓 엘리먼트에 대한 관찰 멈춤
observer.unobserve(entry.target);
}
})
}, options)
// 관찰할 대상을 선언하고, 해당 속성을 관찰
const images = document.querySelectorAll('.image');
images.forEach((el) => {
io.observe(el);
})
무한 스크롤 기능도 IntersectionObserver를 이용하면 쉽게 구현 할 수 있다.
<div class="container">
<div id="items"></div>
<div id="loader"></div>
</div>
컨테이너 역할을 하는 DOM의 하단에 추가 로딩을 위한 element를 추가 한뒤, 해당 element가 뷰포트에 접근했을 때 추가 컴포넌트 또는 이미지를 로딩 하면 된다. 이미지 동적 로딩 때와는 달리, 계속해서 추가 로딩을 해야하기 때문에 unobserve는 하지 않는다.
const count = 20
let index = 0
const loadItems = () => {
const fragment = document.createDocumentFragment()
for (let i = index; i < index + count; i += 1) {
const item = document.createElement('div')
item.innerText = `${i + 1}`
item.classList.add('item')
fragment.appendChild(item)
}
document.getElementById('items').appendChild(fragment)
index += count
}
const observer = new IntersectionObserver(([loader]) => {
if(!loader.isIntersecting) return
loadItems()
})
loadItems()
observer.observe(document.getElementById('loader'))
참고자료