Intersection observer
기본적으로 브라우저 뷰포트(Viewport)와 설정한 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 포함되지 않는지, 더 쉽게는 사용자 화면에 지금 보이는 요소인지 아닌지 구별하는 기능을 제공한다.
이 기능은 비동기적으로 실행되기 때문에, scroll 같은 이벤트 기반의 요소 관찰에서 발생하는 렌더링 성능이나, 이벤트 연속 호출같은 문제가 발생할 일 없이 사용할 수 있다.
사용자가 아래로 스크롤하다가 타깃 요소와 뷰포트(intersection root)가 겹칠 때, 이 중첩 또는 교차의 양을 intersection observer api가 관찰하는 것이다.
target element: 말 그대로 타깃(대상) 요소이다.
intersection root : 대상 요소의 교차점을 관찰할 상위 요소(또는 뷰포트)이다.
intersection ratio : 대상 요소가 교차 루트와 얼마나 교차했는지 측정한다. 값의 범위는 0~1이며, 교차 비율이 0이면 전혀 교차하지 않았다는 것이고, 1이면 완전히 교차함을 나타낸다.
threshold는 intersection ratio가 해당 값에 도달할 때마다, intersection observer가 콜백 함수를 실행하는 값이다. 위의 사진에서 threshold는 0.5이기에, 타깃 요소가 반 정도 걸쳤을 때 콜백함수가 실행된다.
기본적인 사용법
const observer = new IntersectionObserver((entries,observer) => {}, options);
observer.observe(targetDOM);
IntersectionObserver 생성자는 두 가지 매개변수를 받는데, 첫 번째 매개변수는 실행시킬 콜백 함수(entries, observer)를 넣는다, 그리고 두 번째 매개변수로는 옵션을 설정한다.
이후 관찰하고 싶은 DOM을 생성된 observer 함수의 매개변수로 등록하여 관찰을 시작한다.
콜백의 첫 번째 매개변수 (entries)
- entries
IntersectionObserverEntry 인스턴스의 배열이다.
- boundingClientRect: 관찰 대상의 사각형 정보를 반환한다.
- intersectionRect: 관찰 대상과 뷰포트의 교차 영역 정보를 반환한다.
- intersectionRatio: 관찰 대상의 교차한 교차 영역을 0 ~ 1 사이의 값으로 반환한다.
- isIntersecting: 관찰 대상이 현재 루트 안에 포함되어 있는지 여부를 boolean으로 반환한다.
- rootBounds: 루트 요소의 사각형 정보를 반환한다.
- target : 관찰 대상의 요소(Element)를 반환한다.
- time : 관찰 대상의 변경이 발생한 시간 정보를 반환한다.
- observer
콜백이 실행되는 인스턴스를 참조
콜백의 두 번째 매개변수(options)
- root
요소와 교차가 되는 여부를 확인할 때 기준이 되는 요소를 뜻하며, 기본 값은 null이며, 이는 브라우저의 viewport이다.
- rootMargin
바깥 여백을 이용해서 범위를 확장하거나 취소할 수 있다. css에서 사용하는 것과 동일
- threshold
옵저버가 실행되기 위해 타깃 요소와 얼마나 겹쳐야 하는지 백분율로 표시
메서드
observe()
관찰을 시작할 요소를 선택
unobserve()
관찰 중인 요소를 관찰 중지를 한다. 만약 인스턴스에 관찰 중인 요소가 없을 경우 아무런 동작도 하지 않는다.
disconnect()
모든 요소의 관찰을 중지
예시 1
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>예제</title>
<style>
.item{
text-align: center;
padding: 20px 0px;
margin : 0px;
}
.item:nth-child(even){
background-color: lightcoral;
}
.end{
background-color: lightblue;
height: 50px;
text-align: center;
}
</style>
</head>
<body>
<div class="list"></div>
<p class="end">END</p>
<script src="./script.js"></script>
</body>
</html>
const count = 20;
let itemIndex= 0;
let observer = new IntersectionObserver( entries => {
entries.forEach(entry => {
const list = document.querySelector('.list');
if (entry.isIntersecting){
for (let i = itemIndex; i < itemIndex + count; i++){
let item = document.createElement('p');
item.textContent = i;
item.className += 'item';
list.appendChild(item);
}
itemIndex = itemIndex + count;
console.log(itemIndex)
}
})
}, {root: null, threshold: 1});
observer.observe(document.querySelector('.end'));
end라는 class명을 가진 p태그를 관찰한다. 뷰포트와 end가 완전히 겹칠 때마다 아이템이 20개씩 늘어날 것이다.
end라는 파란색 박스가 겹치는 동시에 요소가 추가되는 것을 확인할 수 있다.
이미지 lazy-loading
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>예시</title>
<style>
img {
width: 400px;
height: 450px;
display: block;
margin: 10px auto;
}
</style>
</head>
<body>
<img src="https://via.placeholder.com/400x450"
data-src="http://www.management-mmm.com/data/file/artist/thumb-3666908620_treMg0fB_2952bd79e1a8d9a2ef9c128c47428e04b66cb6cf_498x598.jpg?v=1714226968">
</img>
<img src="https://via.placeholder.com/400x450"
data-src="http://www.management-mmm.com/data/file/artist/thumb-3666908620_treMg0fB_2952bd79e1a8d9a2ef9c128c47428e04b66cb6cf_498x598.jpg?v=1714226968">
</img>
<img src="https://via.placeholder.com/400x450"
data-src="http://www.management-mmm.com/data/file/artist/thumb-3666908620_treMg0fB_2952bd79e1a8d9a2ef9c128c47428e04b66cb6cf_498x598.jpg?v=1714226968">
</img>
<img src="https://via.placeholder.com/400x450"
data-src="http://www.management-mmm.com/data/file/artist/thumb-3666908620_treMg0fB_2952bd79e1a8d9a2ef9c128c47428e04b66cb6cf_498x598.jpg?v=1714226968">
</img>
</body>
<script>
const observer = new IntersectionObserver(function(entries, observer) {
entries.forEach(entry => {
if (entry.isIntersecting){
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
})
},{threshold:1 })
const imgs = document.querySelectorAll('img');
imgs.forEach(img => {
observer.observe(img);
})
</script>
</html>
img 태그와 뷰포트가 완전히 겹쳤을 때, 우주 최고 배우 김태리 배우의 사진이 나오는 것을 확인할 수 있다. intersection observer를 사용하면 이렇게 image lazy-loading도 구현할 수 있다.
'프로그래밍 언어 > JavaScript' 카테고리의 다른 글
[JS] Callback, Promise, Async/Await (0) | 2024.04.29 |
---|---|
[JS] 순수함수(Pure Function) (0) | 2024.04.29 |
[JS] 함수 선언식과 표현식, 즉시 실행 함수(IIFE) (0) | 2024.04.26 |
[JS] 얕은 비교, 깊은 비교 / 얕은 복사, 깊은 복사 (0) | 2024.04.26 |
[JS] 클로저 (2) | 2024.04.25 |