밑에 나오는 discussion 목록이 너무 길어서 10개씩 끊는 페이지네이션을 해보려고 한다.
간단한 페이지네이션 구현하기
Pagination 페이지네이션은 다수의 콘텐츠를 여러 페이지로 나누고, 이전 또는 다음 페이지로 넘어가거나 특정 페이지로 이동할 수 있는 버튼을 제공하는 기술입니다. 페이지네이션은 공통된 주제
nohack.tistory.com
페이지네이션은 생소해서 위 블로그를 참고했다.
HTML에는 맨 밑에
<div class="buttons"></div>
한 줄만 추가하면 된다.
CSS로 디자인을 한다.
/* 페이지네이션 */
.buttons {
margin-top: 50px;
position: relative;
padding: 1rem 0;
display: inline-flex;
justify-content: center;
}
.button {
padding: 0 1rem;
font-size: 1.2rem;
color: #333;
background: transparent;
border: 0;
outline: 0;
cursor: pointer;
}
.button.active,
.button:hover {
color: #579BB1;
font-weight: 600;
text-decoration: underline;
}
.prev {
position: absolute;
left: -2.5rem;
}
.next {
position: absolute;
right: -2.5rem;
}
ion-icon {
padding-top: 0.05rem;
}
ion-icon 은 prev, next 아이콘을 웹에서 불러와서 쓰려고 추가한 것인데, HTML에
<script type="module" src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.js"></script>
링크를 추가해서 불러왔다.
본격적으로 코드를 짜봐야 하는데, 나도 100% 이해하고 따라한 것은 아니다. 내 방식대로 저 블로그의 코드를 해석해서 써보겠다.
1. 변수 세팅
const contents = document.querySelector("ul.discussions__container");
const buttons = document.querySelector(".buttons");
const numOfContent = agoraStatesDiscussions.length;
const maxContent = 10;
const maxButton = 5;
const maxPage = Math.ceil(agoraStatesDiscussions.length / maxContent);
let page = 1;
contents: 글 목록을 담기 위한 부모 요소
buttons: 페이지 버튼을 담기 위한 버튼들의 부모 요소
numOfContent: 전체 discussion의 개수. 나는 배열 data를 가지고 있으므로, 그 배열의 길이를 numOfContent에 할당했다.
maxContent: 한 페이지에 보여줄 discussion의 개수
maxButton: 한 화면에 보여줄 페이지 버튼의 개수(이건 agoraStatesDiscussions의 element가 많아져서 51개 이상이 될 때 의미가 생긴다. 한 페이지에 10개씩 들어가면 5 페이지에 50개이므로, 51개가 되면 6페이지가 생겨야하고 그러면 기존에 1 2 3 4 5 만 보이던 페이지 버튼을 바꿔줘야 하기 때문이다.)
maxPage: 글을 모두 보여주기 위해 필요한 페이지의 개수
page: 현재 페이지 (시작 페이지 = 1)
maxPage 변수에 Math.ceil를 사용한 이유:
전체 discussion 개수를 10으로 나누고 올리면(17.42xxx => 18) 페이지 개수가 나오기 때문
2. 버튼 생성 함수 구현
const makeButton = (id) => {
const button = document.createElement("button");
button.classList.add("button");
button.dataset.num = id;
button.innerText = id;
button.addEventListener("click", (e) => {
Array.prototype.forEach.call(buttons.children, (button) => {
if (button.dataset.num) button.classList.remove("active");
});
e.target.classList.add("active");
renderContent(parseInt(e.target.dataset.num));
});
return button;
};
button 이라는 요소를 만들고 "button"이라는 클래스를 추가한다.
3. 렌더링 함수 구현
여기서부터 이해하는 게 어려웠다. 단순히 10개씩 끊어서 페이지에 집어넣는 개념이 아니라,
이 함수를 통과하면서 모든 자식 요소를 지우고 한 페이지에 10개씩 새로 써넣는 개념이다.
const renderContent = (page) => {
while (contents.hasChildNodes()) {
contents.removeChild(contents.lastChild);
}
for (let id = (page - 1) * maxContent + 1; id <= page * maxContent && id <= numOfContent; id++) {
contents.append(convertToDiscussion(agoraStatesDiscussions[id - 1]))
}
}
const renderButton = (page) => {
// 버튼 리스트 초기화
while (buttons.hasChildNodes()) {
buttons.removeChild(buttons.lastChild);
}
// 화면에 최대 5개의 페이지 버튼 생성
for (let id = page; id < page + maxButton && id <= maxPage; id++) {
buttons.appendChild(makeButton(id));
}
// 첫 버튼 활성화(class="active")
buttons.children[0].classList.add("active");
buttons.prepend(prev);
buttons.append(next);
// 이전, 다음 페이지 버튼이 필요한지 체크
if (page - maxButton < 1) buttons.removeChild(prev);
if (page + maxButton > maxPage) buttons.removeChild(next);
};
const rndr = (page) => {
renderContent(page);
renderButton(page);
};
rndr(page);
이해하면서 쳤지만 한꺼번에 보니 뭐라는지 모르겠다. 하나씩 끊어서 천천히 살펴보자.
const renderContent = (page) => {
while (contents.hasChildNodes()) {
contents.removeChild(contents.lastChild);
}
for (let id = (page - 1) * maxContent + 1; id <= page * maxContent && id <= numOfContent; id++) {
contents.append(convertToDiscussion(agoraStatesDiscussions[id - 1]))
}
}
1. renderContent 라는 함수 표현식을 쓴다. page를 인자로 받는다.
2. 위에서 선언한 contents에 자식노드가 있다면, 마지막 자식부터 지운다. => 그냥 아무것도 없을 때까지 지우겠다는 의미
3. for 문을 이해하려면 우선 makeButton 과 renderButton 함수를 다시 들여다봐야 한다. 나는 '도대체 page 값에 변화를 주는 요인이 무엇인가?'에 대해 이틀 밤낮으로 고민했다. 한 번 가보자.
const makeButton = (id) => {
const button = document.createElement("button");
button.classList.add("button");
button.dataset.num = id;
button.innerText = id;
button.addEventListener("click", (e) => {
Array.prototype.forEach.call(buttons.children, (button) => {
if (button.dataset.num) button.classList.remove("active");
});
e.target.classList.add("active");
renderContent(parseInt(e.target.dataset.num));
});
return button;
};
const renderButton = (page) => {
// 버튼 리스트 초기화
while (buttons.hasChildNodes()) {
buttons.removeChild(buttons.lastChild);
}
// 화면에 최대 5개의 페이지 버튼 생성
for (let id = page; id < page + maxButton && id <= maxPage; id++) {
buttons.appendChild(makeButton(id));
}
// 첫 버튼 활성화(class="active")
buttons.children[0].classList.add("active");
buttons.prepend(prev);
buttons.append(next);
// 이전, 다음 페이지 버튼이 필요한지 체크
if (page - maxButton < 1) buttons.removeChild(prev);
if (page + maxButton > maxPage) buttons.removeChild(next);
};
const rndr = (page) => {
renderContent(page);
renderButton(page);
};
rndr(page);
1. rndr(page) 가 실행되면 page에 1이 할당된 채로 renderButton 함수 실행됨
2. buttons의 모든 자식들 지움(말이 무섭네요)
3. for문을 돌면서 buttons에 makeButton(id)를 append
3-1. page가 1이므로 id의 초기값은 1.
3-2. makeButton(1) 실행
3-3. button이라는 요소 만들고 "button"이라는 클래스 붙여줌
3-4. 버튼 안에 1이라는 값을 넣어줌 (화면에 표시되는 것)
3-5. 버튼의 dataset.num 값을 1로 지정
3-5. dataset.num 값이 없는 모든 버튼의 "active" 클래스를 지움
3-6. 타겟(눌린 버튼)에는 "active" 클래스 넣어줌
3-7. renderContent(1) 실행
3-8. 버튼 "1" 이 buttons에 append 됨
4. 이 방식으로 버튼이 총 5까지 append 된다. (maxButton이 5 이므로)
5. 그리고 append 된 버튼은 클릭됐을 때 자기 dataset.num을 renderContent에 입력하면서 함수를 실행시킨다.
예를 들어 3번 버튼을 누르면 id의 초기값은 21이고 30까지 증가할 수 있으므로 agoraStateDiscussions의 20번째 인덱스부터 29번째 인덱스까지를 화면에 표시할 수 있는 것이다!!!!!!!!!!!!!
버튼의 위치 고정을 위해서 discussion__wrapper 의 높이를 고정시키고 스크롤 기능을 추가했다.
아래는 동작 화면이다.
'부트캠프 > TIL' 카테고리의 다른 글
Section 1 회고 (0) | 2023.01.11 |
---|---|
코드스테이츠 프론트엔드 부트캠프 Day 18 - Coz' Mini Hackathon (5) Local Storage (0) | 2023.01.10 |
코드스테이츠 프론트엔드 부트캠프 Day 18 - Coz' Mini Hackathon (3) 데이터 불러오기 (0) | 2023.01.09 |
코드스테이츠 프론트엔드 부트캠프 Day 18 - Coz' Mini Hackathon (2) 버튼 토글 (0) | 2023.01.09 |
코드스테이츠 프론트엔드 부트캠프 Day 18 - Coz' Mini Hackathon (1) 기본 구조 (0) | 2023.01.09 |