영화 검색 페이지를 만드는 개인 과제를 시작했다. 우선 필수 구현 과제인 api연결, 검색 UI 구현, 영화 정보 카드 리스트 UI 구현, const,let,화살표함수, 배열 메소드, DOM제어하기 모두 하루만에 끝내 두었다.
저 중간에 넓은 구역은 Carousel을 추가할 생각이다.
그 밑에 카드들은 넓게 사용해서 구현했는데 grid
를 통해 한 줄에 4개씩 넣었다.
API 문서를 보면 query params로 언어와 페이지를 받고 있었는데 영화 목록이 뒤에 더 있다는 것을 알게 되었다. 이왕 만든김에 뒤에 영화들 까지 보고 싶지 않은가? 그래서 페이지네이션을 구현해서 사용해보려 했다.
일단 완성은 했다. 어제 하루를 사용하면서 내내 구현 못했는데 오늘은 무지성으로 코딩하기보단 생각이라는 것을 하기 시작했다.
const pagination = (total_pages, start_Page) => {
const pages_per_Group = 10;
let temp_html = "";
const next_btn = `<li><a id="next">></a></li>`;
const prev_btn = `<li><a id="prev"><</a></li>`;
temp_html += prev_btn;
const end_page = Math.min(total_pages, start_Page + pages_per_Group - 1);
for (let i = start_Page; i <= end_page; i++) {
const activeClass = i === start_Page ? "active" : "";
temp_html += `<li><a class="page-number ${activeClass}"" id="page-${i}">${i}</a></li>`;
}
temp_html += next_btn;
return temp_html;
};
export default pagination;
우선은 페이지네이션을 위한 뼈대 부터 만들었다. 시작 지점인 start_Page부터 end_page까지 li
태그와 a
태그를 사용해서 리스트를 만들어 주었다. 그리고 시작 지점을 활성화 시켜야 했기 때문에 start_page와 같다면 active
라는 클래스를 추가하도록 했다.
이 pagination은 prev버튼과 next버튼을 누르면 #pagination-container
내부의 innerHTML을 아예 변경하는 방식을 사용했다. 때문에 next와 prev를 누르면 숫자가 커진만큼 리스트가 변화해야했고 페이지를 입력받아 카드의 데이터도 변경되어야했다. 만들다보니 페이지네이션에 관련한 함수만 6개가 나왔다. 지금보니까 너무 막 짠 감이 없지 않아 있다. 이거 반드시 리팩토링해야만 한다.
const updatePagination = (data, start_page) => {
let box = document.querySelector("#pagination-container");
if (!box) {
box = document.createElement("ul");
box.setAttribute("id", "pagination-container");
cardContainer.appendChild(box);
}
box.innerHTML = pagination(data.total_pages, start_page);
};
우선 #pagination-container
가 없다면 생성하고 있다면 box의 innerHTML에 pagination을 붙여준다.
const handleNextClick = async (data) => {
if (start_page + 10 <= data.total_pages) {
start_page += 10;
updatePagination(data, start_page);
addPaginationEventListeners(data);
setActivePage(start_page);
const newData = await getTopRatedMoviesList(start_page);
const html = makeCards(newData);
cardList.innerHTML = html;
}
};
const handlePrevClick = async (data) => {
if (start_page > 1) {
start_page -= 10;
updatePagination(data, start_page);
addPaginationEventListeners(data);
setActivePage(start_page + 9);
const newData = await getTopRatedMoviesList(start_page);
const html = makeCards(newData);
cardList.innerHTML = html;
}
};
const handleActivePageClick = async (event) => {
if (event.target.classList.contains("page-number")) {
document.querySelector(".page-number.active")?.classList.remove("active");
event.target.classList.add("active");
const newData = await getTopRatedMoviesList(
Number(event.target.innerHTML)
);
const html = makeCards(newData);
cardList.innerHTML = html;
}
};
문제의 코드다. 코드를 좀 더 줄일 수 있을 것 같다. 아무튼 의도는 next와 prev를 눌렀을 때 해당 페이지를 클릭하지 않아도 active class를 주고 페이지의 데이터를 가져와 카드를 만들수 있도록 했다.
handleActivePageClick()
함수는 페이지 숫자를 눌렀을 때 class에 active를 주면서 기존 active를 지우는 동작을 한다. 또한 그에 맞는 데이터를 가져오도록 한다.
const addPaginationEventListeners = (data) => {
const nextBtn = document.querySelector("#next");
const prevBtn = document.querySelector("#prev");
const pageNumbers = document.querySelectorAll(".page-number");
if (nextBtn) {
nextBtn.removeEventListener("click", handleNextClick);
nextBtn.addEventListener("click", () => handleNextClick(data));
}
if (prevBtn) {
prevBtn.removeEventListener("click", handlePrevClick);
prevBtn.addEventListener("click", () => handlePrevClick(data));
}
pageNumbers.forEach((pageNumber) => {
pageNumber.removeEventListener("click", handleActivePageClick);
pageNumber.addEventListener("click", handleActivePageClick);
});
};
만든 리스트들에 click이벤트를 넣어주는 함수다.
const setActivePage = (page) => {
const pageNumbers = document.querySelectorAll(".page-number");
pageNumbers.forEach((pageNumber) => {
if (Number(pageNumber.innerHTML) === page) {
pageNumber.classList.add("active");
} else {
pageNumber.classList.remove("active");
}
});
};
위에 handleNextClick
과 handlePrevClick
함수에 들어있는 함수인데 각 버튼을 눌렀을 때 시작 페이지의 active를 활성화 시켜주기 위한 함수이다. 예를 들어 1~10에서 next를 눌렀을 때 11을 활성화시키는 함수이다. 이후 초기 카드 리스트들을 출력하는 함수에 실행시켜주어서 페이지네이션을 완성했다. 아직 css가 덜 만져져서 숫자에 따라 길이가 뒤죽박죽이긴 한데 이건 내일! 리팩토링도 내일! 내일은 그럼 리팩토링, css, 캐러셀 만들기를 해야겠다.
전부터 페이지네이션이랑 캐러셀을 VanillaJS로 만들어 보고 싶었는데 이번 기회에 만들 수 있었다. 이걸 만들면서 classList도 처음 알게 되었다. 아직 모르는게 많다!