今回の記事では、自作ページネーションのコードをご紹介します。
検索エンジン内には、jQueryやVueなどのフレームワークに依存した実装方法の紹介記事が多かったので、
汎用性を意識して、生のJSで処理を組んでみました。
■ 関連記事 : JavaScriptの基礎習得に役立つ技術書【おすすめ3選】
ゴール
まずは完成形のイメージをご確認ください。(Resultタブのみで閲覧してみてください)
1ページにつき3つの要素を表示させるように実装しました。
See the Pen JavaScriptでページネーション by waibandl321 (@JumCode) on CodePen.
ソースコード
結論だけ先に知りたい方のために、まずはソースコードの全体像を共有します。
JSの親要素部分を書き換えれば、共通して動作します。(※HTML, CSSも併用してください)
// 表示要素 DOM操作用定数
const contents = document.querySelector('.contents');
const redraw_elements = document.querySelectorAll('.contents > div');
// ページネーション DOM操作用の定数
const total_el = document.querySelector('.total_counter');
// const page_counter = document.querySelector('.page_counter');
const prev_btn = document.querySelector('.prev');
const next_btn = document.querySelector('.next');
const count = 3;
// グローバル変数
let current_step;
let index_start;
let index_end;
// ページ数を算出
function split_page(current_step_update)
{
total_step = Math.ceil(redraw_elements.length / count);
if( current_step_update === undefined || current_step === 1) {
current_step = 1;
next_btn_disable(); prev_btn_active();
} else if( current_step_update === total_step ) {
next_btn_active(); prev_btn_disable();
} else {
current_step = current_step_update;
next_btn_disable(); prev_btn_disable();
console.log(current_step);
}
total_el.textContent = current_step + '/' + total_step;
redraw(redraw_elements.length, total_step, current_step, count);
}
// DOMの描画
function redraw(total, total_step, current_step, count)
{
// 現在の表示indexを割り出す
index_start = current_step * count - count;
index_end = current_step * count - 1;
let index_array = [];
for (let i = index_start; i < index_end + 1; i++) {
index_array.push(i);
}
// 一時削除
while( contents.lastChild ) {
contents.lastChild.remove();
}
// 再描画
redraw_elements.forEach((element, index) => {
if(index_array.indexOf(index) != -1) {
contents.appendChild(element);
}
});
}
// ページカウンターの作成
function create_page_counter()
{
for (let i = 1; i < Math.ceil(redraw_elements.length / count) + 1; i++) {
let count_list = document.createElement('li');
count_list.setAttribute('data-counter-id', i);
count_list.classList.add('page_number');
count_list.textContent = i;
page_counter.appendChild(count_list);
}
}
// イベント処理
next_btn.addEventListener('click', () => {
split_page(current_step += 1);
});
prev_btn.addEventListener('click', () => {
split_page(current_step -= 1);
});
// class付与・削除関数
prev_btn_active = () => {
prev_btn.classList.add('disable');
}
prev_btn_disable = () => {
prev_btn.classList.remove('disable');
}
next_btn_disable = () => {
next_btn.classList.remove('disable');
}
next_btn_active = () => {
next_btn.classList.add('disable');
}
// DOMの構築が完了したタイミングでページネーション発火
window.addEventListener('DOMContentLoaded', () => {
split_page();
create_page_counter();
document.querySelectorAll('.page_number').forEach((element, index) => {
element.addEventListener('click', function(e) {
current_step = Number(element.getAttribute('data-counter-id'));
split_page(current_step);
})
});
})
■ 関連記事 : JavaScriptの基礎習得に役立つ技術書【おすすめ3選】
解説
ここからは手順と処理内容について解説していきます。
下記の5つの手順で実装を行なっていけば問題なく実装できます。
処理順
- グローバルな定数、変数を定義する
- DOMの読み込みが完了したらページネーションを発火する
- ページ数を算出する
- DOMを再描画する
- ページネーションのイベント処理を実装する
グローバルな定数、変数を定義する
ページネーションはDOM操作を多用する関係で、使用する要素についてはグローバルな定数・変数で定義しておきます。
表示要素とその親要素、1ページに表示させる要素数、ページネーションの各要素などが該当します。
変数については、各描画処理で割り当てる値が動的に変化するので、グローバル定義では値は定義していません。
// 表示要素 DOM操作用定数
const contents = document.querySelector('.contents');
const redraw_elements = document.querySelectorAll('.contents > div');
// ページネーション DOM操作用の定数
const total_el = document.querySelector('.total_counter');
// const page_counter = document.querySelector('.page_counter');
const prev_btn = document.querySelector('.prev');
const next_btn = document.querySelector('.next');
const count = 3;
// グローバル変数
let current_step;
let index_start;
let index_end;
DOMの読み込みが完了したらページネーションを発火する
ページネーションの処理(関数)を、DOMContentLoadedイベントの発生タイミングで実行するための記述です。
window.addEventListener('DOMContentLoaded', () => {
split_page();
create_page_counter();
document.querySelectorAll('.page_number').forEach((element, index) => {
element.addEventListener('click', function(e) {
current_step = Number(element.getAttribute('data-counter-id'));
split_page(current_step);
})
});
})
ページ数を算出する
ここでは、total_stepという変数に合計のページ数を割り当て、
current_stepで、現在のページ番号を定義しています。
引数のcurrent_step_updateは、ページネーションの「次へ」「前へ」のボタンがクリックされた際に渡される値です。
ページの詳細情報を定義した後に、DOMの再描画関数(redraw())に処理を渡します。
function split_page(current_step_update)
{
total_step = Math.ceil(redraw_elements.length / count);
if( current_step_update === undefined || current_step === 1) {
current_step = 1;
next_btn_disable(); prev_btn_active();
} else if( current_step_update === total_step ) {
next_btn_active(); prev_btn_disable();
} else {
current_step = current_step_update;
next_btn_disable(); prev_btn_disable();
}
total_el.textContent = current_step + '/' + total_step;
redraw(redraw_elements.length, total_step, current_step, count);
}
DOMを再描画する
ここでは一度、DOM要素を全てリセットし、1ページに表示させる要素のみを描画しています。
index_start, index_endでページごとの要素を割り出し、
forEachのループ処理時に、各要素のindex番号と一致した要素のみ描画します。
そうすることで、各ページごとに3要素ずつ表示させることができます。
function redraw(total, total_step, current_step, count)
{
// 現在の表示indexを割り出す
index_start = current_step * count - count;
index_end = current_step * count - 1;
let index_array = [];
for (let i = index_start; i < index_end + 1; i++) {
index_array.push(i);
}
// 一時削除
while( contents.lastChild ) {
contents.lastChild.remove();
}
// 再描画
redraw_elements.forEach((element, index) => {
if(index_array.indexOf(index) != -1) {
contents.appendChild(element);
}
});
}
ページネーションのイベント処理を実装する
「次へ」や「前へ」のボタンが押された際の、イベントを定義しています。
といってもクリックされたら、現在のページ番号に + -して、描画関数を実行するのみです。
// イベント処理
next_btn.addEventListener('click', () => {
split_page(current_step += 1);
});
prev_btn.addEventListener('click', () => {
split_page(current_step -= 1);
});
// class付与・削除関数
prev_btn_active = () => {
prev_btn.classList.add('disable');
}
prev_btn_disable = () => {
prev_btn.classList.remove('disable');
}
next_btn_disable = () => {
next_btn.classList.remove('disable');
}
next_btn_active = () => {
next_btn.classList.add('disable');
}
自作した印象として、結構簡単でした。
これまで、jquery.pagination.jsやPagination.jsのライブラリを使用していましたが、
100行ほどのコードで実装できるのであれば、ライブラリを使用する必要性はないですね。
サイトパフォーマンスも向上するので、
他のライブラリに頼っている実装も、自作できないか検証してみようと思います。
コメント