などの疑問や悩みを解決してまいります。
大量のページやコンテンツを管理するために適切な数だけ表示して残りは別ページに表示させる、または切り替えるということを行います。
その中で代表的なものが「ページネーション」と呼ばれる機能です。
今回は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);
そしてfirstPageButton
にactive
クラスが指定されているかどうかの判定を三項演算子を用いて処理を行っていきます。
classList.contains()
は引数に指定したクラスを持っているかを判定し、true
、false
を返します。
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番目の値が入ります。
計算式は、定数startData
にstartData
を足すというだけです。
startData
は0で、itemsPerPage
は5と設定していますので足すと結果は5になります。
これでn番目の値が5となり、1番目の記事(date[0]
)から5番目の記事(date[4]
)のデータを表示するという情報を用意することができました。
const paginatedData = data.slice(startData, endData);
定数paginatedData
には表示データをstartData
とendData
を引数に入れた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
を使います。
定数date
、text
のspan
タグにそれぞれデータを入れます。
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となります。
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
としています。
続いて無名関数ですが、今回クリックしたボタンを取得し、そのボタンに対して色付け用のクラスを付与したいため、e
(element
の略)という名前の引数を入れています。
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回処理が実行されてボタンが生成されpaginateButton
のid
を持つタグ内に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
にしています。
DOMContentLoaded
はHTMLが完全に読み込まれた時点で発生するイベントです。
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
は、getElementsByClassName
でactive
クラスを持つすべての要素を取得するため、値が配列となって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
となっていたページネーションボタンのひとつ前のボタンの要素を取得することができました!prevButton
にactive
クラスを追加したら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
クラスを持つページネーションボタンの次のボタンのテキスト情報を取得することができました。後の処理については「<
」ボタンの処理と同じになりますので、説明は割愛いたしので「>
」ボタンの処理内容を参考に確認してみてください!