무한 스크롤링에 대해 평소에 궁금했는데 Web API에서 제공하는 IntersectionObserver에 대해 처음 알게되어 글을 쓰는 계기가 되었다.
IntersectionObserver API는 상위 요소 또는 최상위 문서의 viewport와 대상 요소 사이의 변화를 비동기적으로 관찰할 수 있는 수단으로 주로 아래 네가지의 목적에 맞게 사용이 된다.
이 API는 특정 요소가 viewport와의 교차점에 들어가거나 나갈 때 또는 두 요소 간의 교차점이 지정된 양만큼 변화될 때 실행되는 콜백 함수를 등록할 수 있다.
관찰 대상 요소는 기기의 viewport 또는 특정 요소와 교차한다. Intersection Observer API에서는 이 viewport나 특정 요소를 root 요소라고 한다.
viewport와 관련된 교차를 관찰(observe)하기 위해서는 root 를 null 로 지정해야 한다.
Intersection Observer API는 IntersectionObsever 객체를 선언하면서 사용이 된다.
let options = {
root: null,
rootMargin: "0px",
threshold: 1.0,
}
let observer = new IntersectionObserver((entries) => {}, options); // callback, options
options는 observer의 콜백이 언제 호출되는지 제어하는 요소이다.
IntersectionObserver의 callback은 IntersectionObserverEntry 객체와 observe 목록을 받는다.
let callback = (entries, observer) => {
entries.forEach((entry) => {
// 각 엔트리는 관찰된 하나의 교차 변화을 설명합니다.
// 대상 요소:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
if (entry.isIntersection){
console.log("교차되었다.")
} else {
console.log("교차되지 않았다.")
}
});
};
만일 entry가 현재 root와 교차하는지 보기 위해서는 isIntersection 을 확인 하면 된다. 이 값은 boolean으로 교차된다면 true를 아니면 false를 나타낸다.
어떤 개념인지와 속성, 메서드들을 확인해봤는데 어떻게 사용해야 할지 아직 감이 오지 않는다. 예시를 통해서 한번 이해를 해봐야 하므로 React.js로 한 div 박스의 변화를 봐야할 것 같다.
뷰포트 기준 div 요소들이 있는데 만일 요소가 80% 보인다면 css opacity 값을 설정하도록 했다.
React.js 기준으로 작성해 Observer를 useRef로 관리하고 div 리스트들을 ref로 하나씩 observe하도록 설정한 부분이 핵심인듯하다.
// App.js
import { useRef } from "react"
import "./App.css"
const list = ["box1", "box2", "box3", "box4", "box5", "box6", "box7", "box8"];
function App() {
let observerRef = useRef(null);
observerRef.current = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.remove("hide")
} else {
entry.target.classList.add("hide")
}
})
}, {
root: null,
threshold: 0.8
})
const handleObserve = (ref) => {
if (ref && observerRef.current) {
observerRef.current.observe(ref)
}
}
return (
<>
<div className="container">
{
list.map((box) => (
<div className="target hide" ref={(ref) => handleObserve(ref)} data-target={box} key={box}>{box}</div>
))
}
</div>
</>
);
}
export default App;
// App.css
.container {
position: relative;
top: 800px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.target {
width: 100px;
height: 100px;
background-color:lightblue;
margin-top: 10px;
}
.hide {
opacity: 0.5;
}
나는 스크롤을 발생하기 위해서 컨테이너 박스를 일부러 위에서 800px 만큼 떨어트려 동작을 확인하도록 했다.

이렇게 스크롤 하기 전에는 hide class가 있는 반면

일정 스크롤을 내리면 box1은 hide class가 제거되어 opacity 속성이 없어 제대로 보이고 box2는 아직 요소가 80% 보이지 않아 hide class가 남아 있는 것을 볼 수 있다.
IntersectionObserver에 개념만 알고 무지했는데 확실히 예제를 한번 사용해보니 어느정도 감이 잡히는것같다… (MDN이 진짜 잘되어있네..)