마지막 상품이 내가 보고있는 화면에 보이면(보이는 정도를 정할 수 있음) 콜백함수를 실행해서 다음 상품들을 화면에 그려주는 것이 IntersectionObserver를 사용한 무한스크롤의 핵심 아이디어이다.
우선 사용하고 있는 API가 아이템을 얼마나 가져오는지, 다음 아이템들을 가져오려면 어떻게 해야하는지 데이터 자료구조를 보고 확인해야 한다.
나는 next
정보를 활용해서 page를 +1씩 할당한 후 다음 페이지를 불러오기로 했다.
// api.js
import { API_URL } from "./constants";
export default async function getProducts(page) {
try {
const res = await fetch(`${API_URL}/products/?page=${page}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const data = await res.json();
window.localStorage.setItem("page", page);
window.localStorage.setItem(
"next",
data.next === null ? data.next : data.next.replace(/\D/g, "")
);
return data;
} catch (e) {
console.error(e);
}
}
// console.log(data);
{
"count": Int,
"next": null이나 String,
"previous": null이나 String,
"results": [
{
"product_id": Int,
"product_name": String,
"seller": Int,
"seller_store": String,
"image": String,
"price": Int,
"shipping_method": String,
"shipping_fee": Int,
"stock": Int,
"products_info": String,
}]
}
let items = document.querySelector(".list-products").childNodes;
let observeLastItem = (observer, items) => {
let lastItem = items[items.length - 1];
observer.observe(lastItem); // 마지막아이템 관찰함수 실행(2-3)
};
뷰포트 하단에서 마지막 아이템이 options의 조건에 부합할 때 callback을 실행하도록 한다.
let observer = new IntersectionObserver(callback, options);
observeLastItem(observer, items);
import { ProductCard } from "../components/productCard";
import getProducts from "./api";
export default function infiniteScroll() {
let items = document.querySelector(".list-products").childNodes; // 감시대상노드
let callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
let page = parseInt(window.localStorage.getItem("page"));
let next = window.localStorage.getItem("next");
if (next === "null") {
return;
} else if (parseInt(next) === page + 1) {
getProducts(page + 1).then((data) => {
const productList =
document.querySelector(".list-products");
data?.results.forEach((item) => {
const productItem = document.createElement("li");
const productCard = new ProductCard(item);
productItem.appendChild(productCard.render());
productList.appendChild(productItem);
});
});
observer.unobserve(entry.target); // 1페이지의 lastItem 관찰 그만하기
setTimeout(() => {
observeLastItem(observer, items);
}, 3000);
}
}
});
};
const options = { threshold: 0.5 };
// 마지막아이템 찾는 함수 선언
let observeLastItem = (observer, items) => {
let lastItem = items[items.length - 1];
observer.observe(lastItem); // 마지막아이템 관찰함수 실행
lastItem.style.background = "pink"; // 마지막 아이템 표시해서 확인
};
// observer 생성자 함수 선언
let observer = new IntersectionObserver(callback, options);
// 마지막아이템 찾는 함수 실행
observeLastItem(observer, items);
}