今回の記事では、自作ページネーションのコードをご紹介します。
検索エンジン内には、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.jsPagination.jsのライブラリを使用していましたが、
100行ほどのコードで実装できるのであれば、ライブラリを使用する必要性はないですね。

サイトパフォーマンスも向上するので、
他のライブラリに頼っている実装も、自作できないか検証してみようと思います。

カテゴリー: JavaScript