【JavaScript】初心者でも簡単実装!ページネーションの作成方法について解説します。

JavaScript Web制作

【JavaScript】初心者でも簡単実装!ページネーションの作成方法について解説します。

なやむくん
Webページにページネーションを実装したい。

なやむさん
ページネーションはどのような仕組みで動いているか知りたい。

などの疑問や悩みを解決してまいります。

大量のページやコンテンツを管理するために適切な数だけ表示して残りは別ページに表示させる、または切り替えるということを行います。

その中で代表的なものが「ページネーション」と呼ばれる機能です。

今回はJavaScriptでページネーションの作成方法について解説していきます。

完成例

まずは今回作成するページネーションの完成例をご紹介します。

See the Pen ページネーション by koutarou mori (@koutarou-mori) on CodePen.

シンプルなページネーションですが、「<」「>」でページ送りを可能にして、最初のページでは「<」のボタンを、最後のページでは「>」のボタンに対してdisabledを設定し、ボタンを押せなくする処理も行っています。

それでは各処理について解説していきます。

JavaScriptコードの解説

JavaScriptコードを中心に解説していきます。

みつた
完成例のコードを上から順に解説していきます。

要素の取得

/**
 * 要素の取得
 */
const ul = document.getElementById("dataList");
const prev = document.getElementById("prev");
const next = document.getElementById("next");
const active = document.getElementsByClassName("active");

上記のコードでは、使用する要素を取得するコードをまとめています。

document.getElementByIdは、指定したidを持つ要素を取得します。

document.getElementsByClassNameは、指定したクラスを持つ要素をすべて取得します。

みつた
getElementsByClassNameでは複数の値を取得することが想定されるので、配列形式で値が返ってくることに注意してください。

表示させるデータの準備

/**
 * ページネーション用のデータ配列
 */
const data = [
  { date: "2024-04-25", text: "1記事目を投稿しました。" },
  { date: "2024-04-25", text: "2記事目を投稿しました。" },
// 任意のデータを記述する
];

上記のコードでは、表示するデータを配列で用意しています。

定数dataにはそれぞれ任意のデータを入れてください。

みつた
今回は日付とテキストをオブジェクトに格納しています。

一度に表示するデータ数を指定

/**
 * 1ページあたりの表示数
 */
const itemsPerPage = 5;

このitemsPerPage一度に表示するデータ数を設定しています。

今回は値を5としているので、5件ずつデータを表示させることを想定としています。

disabledの指定

/**
 * ページネーションボタンをクリック時のdisabledの指定
 */
const disabledButton = () => {
  /**
   * 最初のページネーションのボタンがactiveの場合は「<」をdisabledに
   */
  const firstPageButton = document.querySelector("#paginateButton button:first-child");
  firstPageButton.classList.contains("active") ? (prev.disabled = true) : (prev.disabled = false);
  /**
   * 最後のページネーションのボタンがactiveの場合は「>」をdisabledに
   */
  const lastPageButton = document.querySelector("#paginateButton button:last-child");
  lastPageButton.classList.contains("active") ? (next.disabled = true) : (next.disabled = false);
};

最初のページでは「<」ボタンが必要ないためdisabledを設定してクリックできなくなるようにしています。「>」ボタンについても同様です。

処理はdisabledButton関数にまとめています。

const firstPageButton = document.querySelector("#paginateButton button:first-child");

firstPageButtonにページネーションの最初のボタンの要素を取得しています。

firstPageButton.classList.contains("active") ? (prev.disabled = true) : (prev.disabled = false);

そしてfirstPageButtonactiveクラスが指定されているかどうかの判定を三項演算子を用いて処理を行っていきます。

classList.contains()は引数に指定したクラスを持っているかを判定し、truefalseを返します。

trueであればdisabledを適用させ、falseであればdisabledを適用させないという処理をしています。

以上の処理を関数化して、このあと解説するコードの中で関数を使用してボタンの活性非活性を制御しています。

データを表示させる関数処理

次に実際にデータをどのような処理を行い表示させているか解説していきます。

なやむくん
なんか計算のような記述もあって理解できるか心配です…。
みつた
大丈夫です!計算といってもとても簡単な計算処理だけなので実際にコードの中身を確認していきましょう。

データ表示の処理を行っている部分は以下のコードになります。

/**
 * データ表示の関数
 */
const displayData = (page) => {
  const startData = (page - 1) * itemsPerPage;
  const endData = startData + itemsPerPage;
  const paginatedData = data.slice(startData, endData);
  ul.innerHTML = ""
  paginatedData.forEach((item) => {
    const li = document.createElement("li");
    const date = document.createElement("span");
    const text = document.createElement("span");
    date.textContent = item.date;
    text.textContent = item.text;
    li.appendChild(date);
    li.appendChild(text);
    ul.appendChild(li);
  });
};
displayData(1);

const displayData = (page) => {

データを表示する関数はこのdisplayData関数で行われます。

引数にはpageという値が入るようにしていますが、この引数にはページネーションボタンの数値が入ってきます。

後ほど説明するので今はそれぐらいのものだと思ってもらえるだけでいいです。

const startData = (page - 1) * itemsPerPage;

定数startDataは、配列データの0番目から5つデータを表示する場合の「0番目」を設定する箇所になります。

引数で渡ってきた数値に対して1引いて、itemsPerPageで設定している値を掛けます。

例えば引数に1が渡ってきたとして計算式は1‐1なので結果は0となり、そこにitemsPerPageの値を掛けても結果は0なので、startDataに入る値は0になります。

データは配列なので、ここまでで0番目からnつのデータを表示するという部分が設定できたというのが分かるかと思います。

次にnつの部分をどのように処理して設定しているかについて解説していきます。

const endData = startData + itemsPerPage;

endDataには先程説明したようにn番目の値が入ります。

計算式は、定数startDatastartDataを足すというだけです。

startDataは0で、itemsPerPageは5と設定していますので足すと結果は5になります。

これでn番目の値が5となり、1番目の記事(date[0])から5番目の記事(date[4])のデータを表示するという情報を用意することができました。

const paginatedData = data.slice(startData, endData);

定数paginatedDataには表示データをstartDataendDataを引数に入れたslice()で配列からデータを取り出しています。

slice()は配列からデータを取り出すことができるメソッドです。

今回で言うとstartDataが0でendDataが5なので、1番目の記事(date[0])から5番目の記事(date[4])を取り出すことができます。

paginatedData.forEach((item) => {}

表示するデータが用意できましたので、次に表示させるための処理を行っていきます。

そのためにはデータそれぞれに対して要素を準備して、表示させる要素内に入れていく必要があります。

配列の各データそれぞれに処理を行うためにはforeach文を使用します。

const li = document.createElement("li");
const date = document.createElement("span");
const text = document.createElement("span");

まずデータを入れるための要素をそれぞれ作成します。

要素を作成するにはdocument.createElement()を使います。

引数に作成したいHTMLを入れることでその要素を作成することができます。

今回は以下のような形で要素を表示していきたいので、liタグを生成し、日付用のspanタグ、テキスト用のspanタグを生成します。

<li>
 <span>2024-04-25</span>
 <span>1記事目を投稿しました。</span>
</li>

date.textContent = item.date;
text.textContent = item.text;

表示するための要素が用意出来たらタグの中に各データを入れていきます。

各データを要素内に入れるためにはtextContentを使います。

定数datetextspanタグにそれぞれデータを入れます。

li.appendChild(date);
li.appendChild(text);
ul.appendChild(li);

順にそれぞれ用意したHTMLタグを親要素の中に入れていきます。

要素の中に要素を挿入する場合はappendChild()を使用します。

まずは定数liの中に定数dateを入れます。すると現時点では以下のようになります。

<li>
 <span>2024-04-25</span>
</li>

次に定数liの中に定数textを入れます。すると現時点では以下のようになります。

<li>
 <span>2024-04-25</span>
 <span>1記事目を投稿しました。</span>
</li>

そして最後に定数ulに入れて完成です。

<ul>
 <li>
  <span>2024-04-25</span>
  <span>1記事目を投稿しました。</span>
 </li>
</ul>

このようにリスト表示が完成しました。

foreachは配列の各データに対して処理を実行しますので、表示するデータの数の分だけこれまでの処理を実行していきます。

ここまでがデータを表示させるまでの処理の説明です。

ページネーションを表示させる関数処理

次はページネーションのボタンの表示させている処理について解説していきます。

表示させている箇所は以下のコードです。

/**
 * ページネーション表示関数
 */
const displayPagination = () => {
  const totalPages = Math.ceil(data.length / itemsPerPage);
  const paginateButton = document.getElementById("paginateButton");
  paginateButton.innerHTML = "";
  for (let i = 1; i <= totalPages; i++) {
    const pageButton = document.createElement("button");
    pageButton.textContent = i;
    pageButton.addEventListener("click", (e) => {
      const allPageButtons = document.querySelectorAll("#paginateButton button");
      allPageButtons.forEach((button) => button.classList.remove("active"));
      e.target.classList.add("active");
      disabledButton();
      displayData(i);
    });
    paginateButton.appendChild(pageButton);
  }
};
displayPagination();

displayPagination関数で処理を行っています。

それでは中身を見ていきましょう。

const totalPages = Math.ceil(data.length / itemsPerPage);

定数totalPagesには、itemsPerPageで割った値をさらにMath.ceil()小数点を繰り上げて代入しています。

今回用意したデータは20記事なので、lengthの値は20になることが分かります。

itemsPerPageは5なので計算式は20÷5となり結果は4となります。

みつた
5件表示にするなら、単純にデータ数を表示数で割った数値がボタンの数になりますね。

const paginateButton = document.getElementById("paginateButton");

paginateButtonというidを持つ要素を取得しています。

要素の取得については説明済みですのでここでは割愛します。

for (let i = 1; i <= totalPages; i++) {}

次にfor文を使用して、先程のデータ表示のようにページネーションボタンを表示させていきます。

totalPagesの値は4なので、4回処理を回していきます。

それでは処理内容を見ていきます。

const pageButton = document.createElement("button");
pageButton.textContent = i;

まずはページネーションボタン専用のbuttonタグを生成します。

buttonタグにはtextContentを使用してそれぞれ数字を入れていきます。

これで1~4の数字が入ったbuttonタグを生成することができます。

pageButton.addEventListener("click", (e) => {}

各ボタンを生成したらそれぞれにクリックイベントを付与していきます。

addEventListener()各イベントが発生したときに処理を実行させることができるメソッドです。

クリックされたボタンは、表示させているページを視認しやすくするためにクラスを追加して色を付ける処理を行っています。

第1引数にイベントの種類を指定するので、今回はclickとしています。

続いて無名関数ですが、今回クリックしたボタンを取得し、そのボタンに対して色付け用のクラスを付与したいため、eelementの略)という名前の引数を入れています。

const allPageButtons = document.querySelectorAll("#paginateButton button");

#paginateButton buttonに該当する要素をすべて取得して、定数allPageButtonsに代入しています。

document.querySelectorAll()は、引数に指定したCSS セレクターに一致する要素を全て取得することができるメソッドです。

allPageButtons.forEach((button) => button.classList.remove("active"));

つづいてallPageButtonsに対してforEach文を使用します。

forEach文は、指定した配列の各データに対して関数処理を実行してくれる構文です。

引数の中にはbuttonと入っていますが、この引数に配列のデータそれぞれが代入され処理を実行していきます。

今回の処理はbutton.classList.remove("active")、すなわちactiveクラスを取り除くという関数がデータそれぞれに実行されます。

みつた
クリックする前には、すでにいずれかページネーションボタンにactiveクラスが付与されている状態なので、一旦すべてのactiveクラスを取り除き、クリックしたボタンにactiveクラスを付与するといった形になります。

e.target.classList.add("active");
disabledButton();
displayData(i);

先程引数に指定したeに対して.targetとすることでクリックしたbuttonの要素に対して何らかの処理を実行することができます。

今回はactiveクラスを付与したいので.classList.add("active");としています。

こうすることでクリックしたボタンにのみ色を付けることができるようになります。

この後はdesabledを再設定するためにdisabledButton();が、押したページネーションボタンのデータを再表示するためにdisplayData(i);をそれぞれ実行しています。

paginateButton.appendChild(pageButton);

そして最後に取得していた定数paginateButton内にpageButtonを追加していきます。

pageButton分の処理が実行されるので、今回で言うと計4回処理が実行されてボタンが生成されpaginateButtonidを持つタグ内にpageButtonが入れられていきます。

ここまでがページネーションボタンの表示処理についてです。

ページ表示されたらページネーションの最初のボタンをactiveクラスを付与する

ページが表示された時にページネーションの最初のボタン(1のボタン)に色付けをしておきたいので、以下の処理を行っています。

/**
 * ページ読み込み時に最初のボタンをアクティブ化
 */
window.addEventListener("DOMContentLoaded", function () {
  const firstPageButton = document.querySelector("#paginateButton button:first-child");
  firstPageButton.classList.add("active");
  prev.disabled = true;
});

window.addEventListener("DOMContentLoaded", function () {

addEventListenerでイベントをDOMContentLoadedにしています。

DOMContentLoadedHTMLが完全に読み込まれた時点で発生するイベントです。

const firstPageButton = document.querySelector("#paginateButton button:first-child");
firstPageButton.classList.add("active");
prev.disabled = true;

定数firstPageButtonにはページネーションの最初のボタンの要素を取得しています。

取得した要素に対してactiveクラスを付与し、最初のボタンがactive化されている状態なので、「<」ボタンはdisabledを設定しています。

「<」ボタンをクリックした際の処理

残りは「<」「>」ボタンの処理について解説していきます。

先に「<」ボタンの処理について解説していきます。処理内容は以下の通りですね。

/**
 * 「<」をクリックした際の処理
 */
prev.addEventListener("click", () => {
  const activeNum = parseInt(active[0].textContent);
  const prevPage = activeNum > 1 ? activeNum - 1 : 1;
  const prevButton = document.querySelector(`#paginateButton button:nth-child(${prevPage})`);
  const allPageButtons = document.querySelectorAll("#paginateButton button");
  allPageButtons.forEach((button) => button.classList.remove("active"));
  prevButton.classList.add("active");
  displayData(prevPage);
  disabledButton();
});

クリックイベントが指定されていますので、「<」ボタンがクリックされたら各処理が実行されるような形になります。

const activeNum = parseInt(active[0].textContent);

定数activeNumに対して、配列activeの0番目の要素の文字情報をparseInt()で数値に変換して代入しています。

なやむさん
あれ、なぜactiveは配列なんでしたっけ…。
みつた
activeは、getElementsByClassNameactiveクラスを持つすべての要素を取得するため、値が配列となってactiveに代入されます。なので配列の値を参照するためにactive[0]としています。

activeクラスが付与されるのはページネーションボタンなので、textContentで取得できるのは数字になります。

数値ではなく数字なので、parseInt()数値に変換してactiveNumに代入しています。

なぜ数値にしているかといいますと、このあとのコードの条件式で簡単計算を行うためです。

なやむくん
また計算ですか…。
みつた
とても簡単な計算なので元気出していきましょう!

const prevPage = activeNum > 1 ? activeNum - 1 : 1;

定数prevPageには、三項演算子を用いた条件式によって結果を分けており、条件に応じた結果を代入しています。

この計算式は、まずactiveNum > 1 の部分が条件式です。

つまりactiveNum が1よりも大きい数値である場合はactiveNum - 1の計算結果を、そうでない場合は1を代入しています。

例えば数字が2のページネーションボタンがactiveである場合は、activeNumの値は2になります。

そのため条件式に当てはめると結果はtrueとなりますので、 activeNum - 1の結果は1となりその値がprevPageに代入されます。

const prevButton = document.querySelector(#paginateButton button:nth-child(${prevPage}));

定数prevButtonには、prevPageに代入されている数値の順番にあるページネーションボタンの要素を取得して代入しています。

よろこびくん
これでactiveとなっていたページネーションボタンのひとつ前のボタンの要素を取得することができました!prevButtonactiveクラスを追加したらOKですね!
みつた
その前に元々あったactiveクラスを削除しておかないと、activeになっているボタンが2つになってしまいますので、元々あるactiveクラスを削除する処理を行います。

const allPageButtons = document.querySelectorAll("#paginateButton button");
allPageButtons.forEach((button) => button.classList.remove("active"));

ページネーション表示関数の処理の説明でも出てきましたが、定数allPageButtonsにすべてのページネーションボタンの要素を取得し、代入しています。

そしてforEach文を使用して、取得した各要素からactiveクラスを削除しています。

prevButton.classList.add("active");
displayData(prevPage);
disabledButton();

ここでprevButtonに対してactiveクラスを付与して上げることでページネーションボタンのactiveクラスの移動処理が完了したことになります。

ページネーションボタンのactiveクラスの移動が完了したので、新たにデータを表示させるためdisplayData(prevPage);と、disabledButton();disabled設定チェックを行います。

みつた
displayData関数の引数にはprevPageを入れることを忘れないように注意してください。

「>」ボタンをクリックした際の処理

最後に「>」ボタンをクリックした際の処理内容について解説していきます。

部分的に「<」ボタンの処理と内容が同じ箇所がありますので、異なる部分のみ解説していきます。

>」ボタンがクリックされた時の処理は以下です。

/**
 * 「>」をクリックした際の処理
 */
next.addEventListener("click", () => {
  const activeNum = parseInt(active[0].textContent);
  const totalPages = Math.ceil(data.length / itemsPerPage);
  const nextPage = activeNum < totalPages ? activeNum + 1 : totalPages;
  const nextButton = document.querySelector(`#paginateButton button:nth-child(${nextPage})`);
  const allPageButtons = document.querySelectorAll("#paginateButton button");
  allPageButtons.forEach((button) => button.classList.remove("active"));
  nextButton.classList.add("active");
  displayData(nextPage);
  disabledButton();
});

const totalPages = Math.ceil(data.length / itemsPerPage);

ここはページネーション表示関数の処理内容の解説でもあった部分とまったく同じなので解説は割愛いたします。

const nextPage = activeNum < totalPages ? activeNum + 1 : totalPages;

定数nextPageには、まずactiveクラスを持つページネーションボタンのテキスト情報を取得して代入しています。

取得した定数nextPageを三項演算子の条件式に当てはめて、activeNumよりもtotalPagesの値のほうが大きい場合はtrueとなるのでactiveNum + 1の処理結果を、そうでない場合はそのままtotalPagesを代入しています。

例えばactiveNumが2として、totalPagesは4とします。(itemsPerPageが5であり、data.lengthは20の想定)

すると条件式に対してtrueになりますので、activeNumに1がプラスされて結果が3となります。

みつた
これでactiveクラスを持つページネーションボタンの次のボタンのテキスト情報を取得することができました。

後の処理については「<」ボタンの処理と同じになりますので、説明は割愛いたしので「>」ボタンの処理内容を参考に確認してみてください!

よろこびくん
ここまで長かったけど、ページネーションの処理内容の理解できた気がします!
みつた
またわからなくなったら戻ってきてください!
  • この記事を書いた人
  • 最新記事

みつた

完全未経験&異業種から30歳の年でIT企業に転職。

Web系開発言語が好き。
どちらかというとバックエンドよりもフロントエンドが好き(現時点では…)

最近はサウナと観葉植物にハマっている。
野球が好きで一応投手。

-JavaScript, Web制作