[VanillaJS] 간단한 페이지네이션 구현하기

초코침·2023년 3월 12일
1

VanillaJS

목록 보기
4/7

한 페이지에 글 목록이 특정 개수만큼만 보여지도록 페이지네이션을 구현해보려 한다.

예시로 한 페이지 당 6개의 글 목록이 나오도록 만들어볼 것이다.

구현 아이디어

우선 페이지네이션을 구현하기 위해 다음과 같은 기능이 필요하다.

  1. 필요한 페이지 번호 수 구하기

    요소의 총 개수를 페이지 당 보여줄 개수만큼 나눠 올림한 값을 구한다. 예를 들어, 한 페이지 당 6개의 목록을 보여주려는 상황에서 전체 글이 25개라면 총 5개의 페이지가 필요하다.

  2. 필요한 페이지 번호 수만큼 페이지 번호 버튼 동적으로 생성하기

    글이 25개라면, 각 1 2 3 4 5 페이지로 이동할 수 있는 페이지 버튼을 만들어야 한다. 글이 삭제되거나 추가되면 필요한 페이지 번호 수가 바뀔 수 있기 때문에 동적으로 생성한다.

  3. 해당 페이지에서 보여줄 목록을 렌더링하기

    전체 데이터에서 해당 페이지 번호에서 보여줄 글의 목록을 생성해야 한다. 글이 25개라면 3번 페이지로 이동했을 때 13번 글부터 18번 글까지 보여줘야 한다.

  4. 페이지 번호 버튼과 이전, 이후 버튼을 클릭했을 때 실행할 이벤트 적용하기

    이전 또는 이후 버튼 클릭 시 이전 또는 다음 페이지로 이동할 수 있어야 하고, 페이지 번호 버튼을 눌렀을 땐 해당 번호 페이지로 이동할 수 있어야 한다.

구현

기초 작업

페이지네이션할 데이터

내 블로그에 있는 글들을 페이지네이션 데이터로 사용해보려 한다. 다음처럼 객체 배열로 구성하였다.

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 },
];

html

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

글 목록을 구분해낼 수 있을 만큼 (참고용으로 )최소한의 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번째 글까지 보여주면 된다.

함수를 호출하면서 인수로 넣어준 pageNumbercurrentPage 변수에 담는다. 그리고 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번째 글을 보여준다

click 이벤트 달기

페이지 번호 버튼 클릭 이벤트

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를 적용하였음)

페이지 번호에 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의 클릭 리스너에서 호출해주면 된다.

전체 코드 보러가기(코드를 좀 더 정리하여 첨부했습니다)


페이지네이션 끝!

profile
블로그 이사중 🚚 (https://sungjihyun.vercel.app)

0개의 댓글