윈도우의 이벤트리스너로 스크롤을 지정하여서 화면의 마지막에 다다르면 함수를 호출하는 형식이다.
window.addEventListener("scroll", () => {
const { isLoading, totalCount, photos } = this.state;
// 현재 보이는 브라우저 창의 높이, 맨위에서 스크롤된 위치, 전체 문서의 높이
const isScrollEnded =
window.innerHeight + window.scrollY + 100 >= document.body.offsetHeight;
if (isScrollEnded) {
onScrollEnded();
}
});
}
사용자가 화면에 마지막에 다다랐을 때를 계산하기 위해서 세 가지 값을 이용하였다.
window.innerHeight
브라우저 창의 높이(창의 상단바나 주소바 포함 x)
window.scrollY
문서의 맨 위부터 현재 스크롤된 위치 까지의 거리
document.body.offsetHeight
<body> 요소의 전체 높이
window.innerHeight + window.scrollY + 100 >= document.body.offsetHeight
브라우저 창의 높이와 현재 스크롤된 위치 까지의 거리를 더하면 사용자가 현재 보고있는 화면하단 까지의 높이를 구할 수 있어서 가장 하단을 보고 있는지 아닌지 감지할 수 있다.
하지만 이 방법은 스크롤 할 때마다 이벤트를 호출하므로 성능상 좋지 않고, 비교적 구현하기가 그렇게 간단하지가 않아서 intersection observer를 더 많이 사용한다.
intersection observer를 통해 사용자가 특정 요소를 보고 있는지 감지할 수 있다.
export default function PhotoList({ $target, initialState, onScrollEnded }) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
// 현재 감시하고 있는 엔트리가 보이는지
if (entry.isIntersecting && !this.state.isLoading) {
console.log("화면 끝!!");
if (this.state.totalCount > this.state.photos.length) {
onScrollEnded();
}
}
});
},
{
root: null, // 대상 요소를 감시할 상위 요소
threshold: 0.5, // 화면이 얼마나 보였을때에 따라 달라지는지
}
);
let $lastLi = null;
this.render = () => {
// 마지막 이미지가 시작될때마다 불러옴
const $nextLi = $photos.querySelector("li:last-child");
// 마지막 요소를 옵저버에 등록
if ($nextLi !== null) {
if ($lastLi !== null) {
observer.unobserve($lastLi);
}
$lastLi = $nextLi;
observer.observe($lastLi);
}
};
this.render();
}
우선 가독성을 위해 많은 요소들을 삭제하였다.
기본적으로 render를 호출할때마다 사진요소들을 5개씩 덧붙여서 렌더링하는 방식이다.
최초에 사진 5개를 렌더링하고, 마지막 사진 요소를 observer.observe를 통해 감시 등록을 한다.
그리고 다음 렌더링 시 사진 5개가 추가적으로 렌더링되고, 이전의 5번째 마지막 사진 요소를 observer.unobserve 를 통해 감시 해제를 하고 10번째 마지막 사진 요소를 다시 감시 등록을 한다. 이런식으로 계속 반복된다.
다음으로 옵저버를 선언하는 곳을 보자. 여기서 옵저버에 대한 이벤트를 관리한다.
entry는 observer가 등록한 요소들을 나타내고, entry.isIntersecting은 사용자가 이 요소들을 보고 있을때 true를 반환한다. 사진을 서버에서 모두 받아왔을때에는 새로 이미지를 받아오는 함수를 호출하지 않기 위해서 현재 받아온 이미지 개수가 서버에서 받아온 이미지 보다 작아야만 하는 조건을 걸었다.
다행히 이번에는 코드를 이해하는데 엄청 어렵진 않았다..! 평소에 두가지 방법 모두 모르고 있어서 얻어갈 수 있는 것이 많았고, 두번째 방법은 사용하기 정말 편한 것 같다.