한 페이지에 글 목록이 특정 개수만큼만 보여지도록 페이지네이션을 구현해보려 한다.
예시로 한 페이지 당 6개의 글 목록이 나오도록 만들어볼 것이다.
우선 페이지네이션을 구현하기 위해 다음과 같은 기능이 필요하다.
필요한 페이지 번호 수 구하기
요소의 총 개수를 페이지 당 보여줄 개수만큼 나눠 올림한 값을 구한다. 예를 들어, 한 페이지 당 6개의 목록을 보여주려는 상황에서 전체 글이 25개라면 총 5개의 페이지가 필요하다.
필요한 페이지 번호 수만큼 페이지 번호 버튼 동적으로 생성하기
글이 25개라면, 각 1 2 3 4 5 페이지로 이동할 수 있는 페이지 버튼을 만들어야 한다. 글이 삭제되거나 추가되면 필요한 페이지 번호 수가 바뀔 수 있기 때문에 동적으로 생성한다.
해당 페이지에서 보여줄 목록을 렌더링하기
전체 데이터에서 해당 페이지 번호에서 보여줄 글의 목록을 생성해야 한다. 글이 25개라면 3번 페이지로 이동했을 때 13번 글부터 18번 글까지 보여줘야 한다.
페이지 번호 버튼과 이전, 이후 버튼을 클릭했을 때 실행할 이벤트 적용하기
이전 또는 이후 버튼 클릭 시 이전 또는 다음 페이지로 이동할 수 있어야 하고, 페이지 번호 버튼을 눌렀을 땐 해당 번호 페이지로 이동할 수 있어야 한다.
내 블로그에 있는 글들을 페이지네이션 데이터로 사용해보려 한다. 다음처럼 객체 배열로 구성하였다.
const data = [
{ title: '패턴', postNumber: 1 },
{ title: '마음가짐', postNumber: 2 },
{ title: 'HTML 기초', postNumber: 3 },
{ title: 'css 기초', postNumber: 4 },
{ title: 'CSSflex', postNumber: 5 },
{ title: 'Mac 계산기 클론하기', postNumber: 6 },
{ title: 'CSS grid', postNumber: 7 },
{ title: 'slice? splice', postNumber: 8 },
{ title: '함수를 정의하는 방법', postNumber: 9 },
{ title: '순열(Permutation) 구현하기', postNumber: 10 },
{ title: '문자열에서 특정 위치의 문자를 변경하고 싶은 경우', postNumber: 11 },
{ title: 'letIt const', postNumber: 12 },
{ title: 'nvm', postNumber: 13 },
{ title: '요소 노드의 텍스트 조작하기(nodeValue, textContent, innerHTML)', postNumber: 14 },
{ title: '자바스크립트의 배열은 배열이 아니다!', postNumber: 15 },
{ title: 'JSON 다루기(JSON.parse), JSON.stringify())', postNumber: 16 },
{ title: '원시 자료형과 참조 자료형', postNumber: 17 },
{ title: '[]===[]는 왜 false인가', postNumber: 18 },
{ title: 'this (동적 바인딩)', postNumber: 19 },
{ title: '유효성 검사를 포함한 간단한 회원가입 폼 페이지 만들기', postNumber: 20 },
{ title: '#fff와 #ffffff', postNumber: 21 },
{ title: '안다고 생각했지만, 헷갈렸던 문법들', postNumber: 22 },
{ title: 'DOM이 뭔가요?', postNumber: 23 },
{ title: '영화 좌석 예약 페이지 만들기 ', postNumber: 24 },
{ title: '테두리가 두 줄처럼 보이는 경우 (border)', postNumber: 25 },
{
title: 'IS0 형식의 날짜(yyyy-mm-ddThh:mm:ssz)를 현재 위치 시간대로 변경하기 # ',
postNumber: 26,
},
{ title: '페이지네이션', postNumber: 27 },
];
ul 안에 li를 동적으로 생성해 다음처럼 글 번호와 글 제목을 담은 요소를 목록으로 보여줄 것이며, ul 아래에 페이지를 이동할 수 있는 버튼들을 생성해 두었다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<script src="paginationData.js"></script>
<title>Document</title>
</head>
<body>
<ul>
<li>
<div class="post-container">
<p class="post-number">1</p>
<p class="post-title">title</p>
</div>
</li>
</ul>
<div class="pagination-container">
<div class="prev-button">이전</div>
<div class="number-button-wrapper"><span class="number-button">1</span></div>
<div class="next-button">이후</div>
</div>
<script src="pagination.js"></script>
</body>
</html>
글 목록을 구분해낼 수 있을 만큼 (참고용으로 )최소한의 css만 작성하였다.
* {
margin: 0;
padding: 10px;
box-sizing: border-box;
}
li {
list-style-type: none;
}
.post-container {
display: flex;
align-items: center;
border-radius: 10px;
border: 2px solid #d7d7d7;
margin: 15px;
}
.post-title {
font-size: 20px;
}
.post-number {
font-size: 15px;
padding: 15px;
}
.pagination-container {
display: flex;
justify-content: center;
}
데이터를 몇 개의 페이지로 나눠야할지 구해야 한다. 전체 데이터 개수를 단위 개수로 나눈 후 올림한다.
const COUNT_PER_PAGE = 6; // 한 페이지 당 최대 6개의 요소를 보여줄 것
const getTotalPageCount = () => {
return Math.ceil(data.length / COUNT_PER_PAGE);
};
페이지 번호 버튼 wrapper에 필요한 페이지 번호 버튼을 추가한다.
const numberButtonWrapper = document.querySelector('.number-button-wrapper');
const setPageButtons = () => {
numberButtonWrapper.innerHTML = ''; // 페이지 번호 wrapper 내부를 비워줌
for (let i = 1; i <= getTotalPageCount(); i++) {
numberButtonWrapper.innerHTML += `<span class="number-button"> ${i} </span`;
}
};
setPageButtons()
호출 시 다음처럼 1부터 5까지의 페이지 번호가 생긴다.
인수로 이동할 페이지 번호를 넘겨주면 n번째 글부터 n+5번째 글까지 보여주도록 한다. 즉, 3번 페이지일 경우 13번째 글부터 18번째 글까지 보여주면 된다.
함수를 호출하면서 인수로 넣어준 pageNumber
를 currentPage
변수에 담는다. 그리고 ul
요소를 가져와서 li
요소를 만든 다음 append 한다.
const ul = document.querySelector('ul');
let currentPage = 1;
const setPageOf = (pageNumber) => {
ul.innerHTML = ''; // ul 리스트 내부를 비워줌
for (
let i = COUNT_PER_PAGE * (pageNumber - 1) + 1;
i <= COUNT_PER_PAGE * (pageNumber - 1) + 6 && i <= data.length;
i++
) {
const li = document.createElement('li');
// 컨테이너
const postContainer = document.createElement('div');
postContainer.className = 'post-container';
// 글 번호
const postNumber = document.createElement('p');
postNumber.className = 'post-number';
// 글 제목
const postTitle = document.createElement('p');
postTitle.className = 'post-title';
postNumber.textContent = data[i - 1].postNumber;
postTitle.textContent = data[i - 1].title; // 페이지 번호는 1부터 시작하지만 배열 인덱스는 0부터 시작하므로 -1 해 준다.
postContainer.append(postNumber, postTitle); // 컨테이너 구성
li.append(postContainer); // li 구성
ul.append(li); // ul에 li 자식 요소로 넣어주기
}
};
setPageOf(3)
을 호출하면 다음과 같이 13번째 글부터 18번째 글을 보여준다
querySelectorAll
로 모든 페이지 번호 버튼들을 가져와 click
이벤트 리스너를 달아주었다. 클릭 이벤트 발생 시 number로 형변환한 다음, 해당 페이지를 셋팅하기 위해 setPageOf
함수를 호출한다.
const pageNumberButtons = document.querySelectorAll('.number-button');
pageNumberButtons.forEach((numberButton) => {
numberButton.addEventListener('click', (e) => {
setPageOf(+e.target.innerHTML);
});
});
각 페이지 번호 버튼에 클릭 이벤트를 발생시켜보면 다음처럼 페이지네이션이 되었음을 확인할 수 있다. (페이지 번호 클릭이 잘 보이도록 별도 css를 적용하였음)
이전 버튼을 눌렀을 때 현재 페이지가 2페이지 이상이라면 현재 페이지의 바로 앞 페이지로 이동하고, 이후 버튼을 눌렀을 때 현재 페이지가 최대 페이지보다 작다면 현재 페이지의 바로 다음 페이지로 이동한다.
const prevButton = document.querySelector('.prev-button');
const nextButton = document.querySelector('.next-button');
prevButton.addEventListener('click', () => {
if (currentPage > 1) {
currentPage -= 1;
setPageOf(currentPage);
}
});
nextButton.addEventListener('click', () => {
if (currentPage < getTotalPageCount()) {
currentPage += 1;
setPageOf(currentPage);
}
});
이전 또는 이후 버튼에 클릭 이벤트를 발생시켜보면 다음처럼 페이지네이션이 되었음을 확인할 수 있다. (클릭이 잘 보이도록 별도 css를 적용하였음)
페이지를 이동시킬 때마다 해당 페이지에 해당하는 버튼 색깔을 변경해주려 한다. selected
라는 클래스로 css를 작성한 다음 페이지 번호가 바뀌었을 때 이전 페이지 버튼에서 selected를 제거하고 새로 변경된 페이지 버튼에 selected를 추가한다.
const moveSelectedPageHighlight = () => {
pageNumberButtons.forEach((numberButton) => {
if (numberButton.classList.contains('selected')) {
numberButton.classList.remove('selected');
}
});
pageNumberButtons[currentPage - 1].classList.add('selected');
};
그리고 위 함수를 prevButton
, nextButton
, pageNumberButtons
의 클릭 리스너에서 호출해주면 된다.
전체 코드 보러가기(코드를 좀 더 정리하여 첨부했습니다)
페이지네이션 끝!