【套件筆記 - JS】頁碼 Pagination.js

更新於 發佈於 閱讀時間約 30 分鐘

Pagintaion 套件 + Axios API 請求範例

demo

demo


一、官網資源

pagination 官網:https://pagination.js.org/docs/index.html

可由此處下載 css 原始檔客製 https://pagination.js.org/docs/index.html#Theme


二、CDN 安裝

頁碼 pagination.scss(非必要引入,也可以從官網下載原始檔更改)

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/paginationjs/2.6.0/pagination.min.css">

jQuery Import(使用 slim.min.js 版本會出錯)

<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>

頁碼 pagination.js

<script src="https://cdnjs.cloudflare.com/ajax/libs/paginationjs/2.6.0/pagination.min.js"></script>

Bootstrap 5 CSS(建立表格用,非必要)

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">


三、使用範例

https://codepen.io/annchou_illu/pen/oNmZxmd

以下以政府 API 做為資料來源,搭配 Bootstrap 5 切版

3-1. 目標功能

  1. 頁碼與資料渲染
  2. 簡單篩選功能
  3. 點選至多 3 筆資料

3-2. 示範步驟

  1. 規劃版位
  2. 變數、原始資料宣告、按鈕綁定
  3. 撰寫初始化渲染函式 renderData
    .原始資料請求
    .篩選資料生成
    .頁碼生成
  4. 拆解元件
  5. 加入點選物件功能

3-3. 實作開始

  1. 規劃版位 html
    最主要是先建立好渲染資料和頁碼生成位置,完整 html 可見 codepen
<!-- 被點選物件顯示區 -->
<p class="bg-light">你選擇的專案 seq:<span class="showCheckedList"></span></p>

<!-- 篩選按鈕區 -->
<div class="d-flex justify-content-end gap-1 mb-3">
<input type="button" class="btn btn-secondary btnFilter" value="全部" />
<input type="button" class="btn btn-secondary btnFilter" value="前鎮區" />
<input type="button" class="btn btn-secondary btnFilter" value="三民區" />
</div>

<!-- 資料生成位置與內容模板 -->
<div id="pagination-Container">
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between">
<div>
<p>專案名稱</p>
<p>專案區域</p>
</div>
<input class="form-check-input" type="checkbox" id="checkProject" />
</li>
</ul>
</div>
<!-- 頁碼 -->
<div id="pagination-Pages" class="d-flex justify-content-end mt-3"></div>
  1. 基本前置設定
    於 JavaScript 準備 API 路徑來源宣告,以及將 DOM 元素綁定監聽。
const _url =
"https://api.kcg.gov.tw:443/api/service/Get/1f2a6afe-f953-436f-981c-92f2739b3475";
// 顯示選取專案
const showCheckedList = document.querySelector(".showCheckedList");
const checkedProjectList = []; // 被選取清單

// 初始篩選分類
let selectedFilter = "全部";
// 按鈕篩選處理
const btnFilter = document.querySelectorAll(".btnFilter");
// 按鈕監聽
btnFilter.forEach((btn) => {
btn.addEventListener("click", function () {
selectedFilter = btn.value; // 當按鈕按下,selectedFilter 的值會被改變
renderData(); // 每次被按下都會一再觸發 renderData()
});
});
  1. 在準備渲染前,先來了解 Pagination.js 和 jQuery 文件。
    - Pagination.js Asynchronous or JSONP:https://pagination.js.org/index.html
    - Pagination.js ajax:https://pagination.js.org/docs/index.html#ajaxFunction
    - jQuery ajax:https://api.jquery.com/jquery.ajax/
  2. Pagintaion.js docs/commenlyused 第三項提到 ajax 的請求格式如下。並且套件支援以 jQuery ajax 操作 API 來源資料,如果請求成功則使用 success 接收並繼續執行,請求失敗則跳到 error
dataSource: function(done){
$.ajax({
type: 'GET',
url: '/test.json',
success: function(response){
done(response);
}
});
}
  1. 建立初始化 function renderData 基本架構
    這時就可以組合出大概的渲染函式。
function renderData() {
$(function () {
(function (name) {
let container = $("#pagination-" + name); // 這裡面包裝了套件所需設定,暫時不用理會
$.ajax({
url: _url, // 資料來源 API 網址
success: function (data) {
... // 請求成功後執行
},
error: function (error) {
console.error(error); // 請求失敗時執行
}
});
})("Pages");
});
}

renderData(); // 一啟動網頁就運行
  1. 儲存 API 請求回傳的原始資料
    使用 console 測試,發現我們需要撈回的資料被放在 data 中的 data 屬性,所以 data.data 是我們所需要的,所以宣告一個變數儲存它。
success: function (data) {
const allData = data.data​ //宣告一個 allData 變數來存放 API 回傳的全部資料
},
  1. 撰寫篩選按鈕動作
    因為我們在 html 中寫了三個按鈕,讓它可以做區域篩選,並且上面已經先宣告了變數 selectedFilter,預設值為「全部」。當 selectedFilter 變數的值已經不是「全部」,則將 filteredData 重新賦值成符合篩選條件的資料。
let filteredData = []; // 真正要被渲染的已篩選資料
selectedFilter !== "全部" // 我們在步驟 2 時宣告過全域,並設定初始值為"全部"
? (filteredData = allData.filter((item) => item.name === selectedFilter))
: (filteredData = allData);
  1. 將按鈕加入點擊監聽,測試上述的 selectedFilter 有沒有因點擊而更改。因為每次點擊按鈕都要重新利用 renderData 函式處理,所以我們在執行函式中再度呼叫 renderData
btnFilter.forEach((btn) => {
btn.addEventListener("click", function () {
selectedFilter = btn.value;
renderData();
});
});
  1. 目前我們的 renderData 函式全部是以下狀態,並且加入 console 測試 selectedFilterfilteredData 值是否有跟著按鈕一起改變。確定成功就可以將 console 刪除。
function renderData() {
$(function () {
(function (name) {
let container = $("#pagination-" + name); // 這裡面包裝了套件所需設定,暫時不用理會
$.ajax({
url: _url, // 資料來源 API 網址
success: function (data) {
const allData = data.data​ // 宣告一個 allData 變數來存放 API 回傳的全部資料

let filteredData = []; // 真正要被渲染的已篩選資料
selectedFilter !== "全部"
? (filteredData = allData.filter((item) => item.name === selectedFilter))
: (filteredData = allData);

console.log(selectedFilter)
console.log(filteredData)
},
error: function (error) {
console.error(error); // 請求失敗時執行
}
});
})("Pages");
});
}
  1. 計算頁碼與設定
    接著用篩選後的資料計算可以生成幾頁頁碼,以下程式碼可以接在剛才 filteredData console 的下面。回到套件 docs/methods,發現我們可以透過 container.pagination({ ... }) 設定套件呈現細節。以下屬性詳細請參照官網文件。
container.pagination({
dataSource: filteredData, //我們剛才建立的實際渲染資料
//locator: "data", // 資料來源中的屬性,因為上面使用的 filterData 已經是處理過的資料,所以可忽略設定
totalNumber: filteredData.length, //實際資料總數
pageSize: 5, //每頁資料數
showPageNumbers: true,
showPrevious: true,
showNext: true
});
  1. 渲染頁碼與資料
    回到官網文件 docs/dataSourcescontainer 中的callback 屬性可用於插入自訂的 html,所以我們可以將上面 html 要渲染的資料 html 模板貼下來。input 中的各項屬性視原始資料內容加上獨特值。
container.pagination({
//...略

//頁碼渲染與監聽
callback: function (res, pagination) {
let dataHtml = '<ul class="list-group">';

$.each(res, function (index, item) {
dataHtml += `<li class="list-group-item d-flex justify-content-between">
<div>
<p>${item.projectName}</p>
<p>${item.name}</p>
</div>
</li>`;
});

dataHtml += "</ul>";
$("#pagination-Container").html(dataHtml); // 設定 HTML 內容到 container​
}
});
  1. 最後補上執行 renderData,讓網頁一開啟就運行。就完成目標功能的第一二項,已經可以作頁碼切換與按鈕篩選資料了。
function renderData(){...}

renderData();
  1. 拆元件
    因為目前 function renderData 內的程式碼冗長,所以先來做拆分整理。我們剛才在 function renderData 內的程式碼大致可以分為
    .基礎架構
    .資料依據按鈕篩選:需要輸入資料 allData,並生成 filteredData
    .將篩選過的資料計算頁碼:需要輸入這兩筆資料 filteredDatacontainer
    .渲染頁碼與資料:需要輸入資料 res
    依據以上分類可以拆成四個函式
// 基礎架構
function renderData() {
$(function () {
(function (name) {
let container = $("#pagination-" + name);
$.ajax({
url: _url,
success: function (data) {
// 將完整資料交給 handleFilterData 函式篩選
const allData = data.data
const filteredData = filterData(allData);
// 將返回的 filteredData 計算頁碼
handlePagination(filteredData, container);
},
error: function (error) {
console.error(error);
}
});
})("Pages");
});
}
// 資料依據按鈕篩選:需要輸入資料 allData,並生成 filteredData。​
function filterData(data) {
let filteredData = [];
selectedFilter !== "全部"
? (filteredData = data.filter((item) => item.name === selectedFilter))
: (filteredData = data);
return filteredData;
}
//使用篩選資料,計算頁碼
function handlePagination(filteredData, container) { container.pagination({
dataSource: filteredData, // 實際資料來源
//locator: "data", // 資料來源中的屬性,因為上面使用的 filterData 已經是處理過的資料,所以可忽略設定
totalNumber: filteredData.length, // 實際資料總數
pageSize: 5, // 每頁資料數
showPageNumbers: true,
showPrevious: true,
showNext: true,

// 頁碼渲染與監聽
callback: function (res, pagination) {
//渲染頁碼與資料
renderPagination(res); }
});}
// 渲染頁碼與資料
function renderPagination(res) {
let dataHtml = '<ul class="list-group">';
$.each(res, function (index, item) {
dataHtml += `<li class="list-group-item d-flex justify-content-between">
<div>
<p>${item.projectName}</p>
<p>${item.name}</p>
</div>
</li>`;
});

dataHtml += "</ul>";
$("#pagination-Container").html(dataHtml); // 設定 HTML 內容到 container
}
  1. 加入點選功能的 checkbox
    先從渲染 checkbox 開始,修改剛才建立好的 function renderPagination 中的 dataHtml
$.each(res, function (index, item) {
dataHtml += `<li class="list-group-item d-flex justify-content-between">
<div>
<p>${item.projectName}</p>
<p>${item.name}</p>
</div>
<input class="form-check-input" type="checkbox">
</li>`;
});
  1. 為 checkbox 加入辨識值
    每一個 checkbox 都需要被辨識,觀察 $.each(res, function (index, item) 中的 item 可以發現 item.projectSEQ 這個唯一值可以被使用,所以我們可藉由 data-*MDN) 的方式加入此值。
$.each(res, function (index, item) {
dataHtml += `<li class="list-group-item d-flex justify-content-between">
<div>
<p>${item.projectName}</p>
<p>${item.name}</p>
</div>
<input class="form-check-input" type="checkbox"
id="checkProject${item.projectSEQ}"
data-seq=${item.projectSEQ}> //這裡加入 data-seq
</li>`;
});
  1. 儲存被點選的 checkbox
    完成以上步驟後,我們已經可以點選 checkbox,也可以換頁。但如果回到上一頁,會發現剛才點選的 checkbox 狀態因為換頁資料重新渲染,導致失去被點選狀態。因此我們要紀錄有哪些 checkbox 被點選過,之後才能在渲染時判定每個 checkbox 該呈現什麼狀態。
    步驟 2 中,我們有宣告一個 checkedProjectList 變數用來儲存被點選的 checkbox 清單。現在我們建立一個函式來將資料寫入變數中。判讀被點選的 checkbox data-seq,將 seq 存入。讀取 data-* 的方法使用 checkbox.getAttribute("data-seq"),或 checkbox.dataset.seq 都可以。
    另外特別注意的是,我們從 API 取得回來的 projectSEQ 參數是 number 型別,所以我們在這裡存入checkedProjectList 變數的值也必須轉成 number 型別才不會之後影響判斷。
function handleCheckProject() {
// 將所有 checkbox 綁定
const checkBoxes = document.querySelectorAll("input[type='checkbox']");

// 因為 checkBoxes 有多個 checkbox,所以必須先使用 forEach 才能 addEventListener 監聽
checkBoxes.forEach(checkbox => {
checkbox.addEventListener("change", () => {
const projectSeq = checkbox.getAttribute("data-seq");
checkedProjectList.push(+projectSeq) // 使用「+」轉 number

//因為步驟 2 中已經有做 DOM 綁定,所以在這裡將儲存的資料渲染
showCheckedList.textContent = checkedProjectList.join("、");
})
})
}
  1. 回到 function handlePagination 函式,在 callback 處加入 handleCheckProject(),讓它能夠執行。
function handlePagination(filteredData, container){
...

callback: function (res, pagination) {
...

//處理 checkbox 選擇專案
handleCheckProject();
}
});
}
Demo

Demo

  1. 加入判斷當前 checkbox 是否已被點選,生成樣板
    回到 function renderPagination 函式,在進入 each 之後,執行當前 item.projectSEQ 值是否存在於 checkedProjectList 變數中。includes 會返回 true/falseMDN)。
    checkbox (MDN)文件中提到如果是被勾選狀態,則會被加入 checked 在 HTML 中標示,所以也在 dataHTML 中使用三元運算子根據剛才 isChecked 的結果判斷是否加入 checked
function renderPagination(res) {
let dataHtml = '<ul class="list-group">';
$.each(res, function (index, item) {
// 是否已經被選取
const isChecked = checkedProjectList.includes(item.projectSEQ);

dataHtml += `<li class="list-group-item d-flex justify-content-between"><div><p>${
item.projectName
}</p><p>${
item.name
}</p></div><input class="form-check-input" type="checkbox" id="checkProject${
item.projectSEQ
}" data-seq=${item.projectSEQ}
${isChecked ? "checked" : ""}> // 三元運算子判斷當前 checkbox 狀態
</li>`;
});

dataHtml += "</ul>";
$("#pagination-Container").html(dataHtml); // 設定 HTML 內容到 container
}
  1. 完成後的資料表即使換頁再回上一頁,也能繼續顯示相同 checkbox 勾選狀態了。但還有一些小錯誤,即使 checkbox 取消,或是再次點選都會不斷累積相同 seq 編號在 checkedProjectList 變數中。
  2. 限制選取資料數量
    我們要解決上述的 bug 以及完成最後一個任務:限制最多只能選取 3 筆資料。回到 function handleCheckProject,我們要在這裡根據 checkedProjectList 變數內的資料數量判斷是否還能再繼續點選動作。
    ■ 我們可能有幾種情境及對應執行:
    (1)已達最多選取數 → 禁止用戶點選,但因為 checkbox 沒有 disable 屬性,所以我們只能讓 checked 強制取消
    (2)未達最多選取數 → 加入清單
    (3)清單中已有目前點選的 seq 編號 → 判斷為取消點選,從清單中移除
    (4)清單中沒有目前的 seq 編號 → 加入清單
    ■ 綜合以上情境狀況,我們把要情境總結成
    (1)已達最多選取數,並且清單中不存在此 seq → 取消當前 checkbox 勾選狀態
    (2)未達最多選取數 → 根據清單是否存在 seq,執行加入清單或刪除該值的動作​,
    以上不管是判斷清單中是否存在當前 seq,或是執行刪除,都會需要知道該 seq 在清單中的位置,所以可以使用 indexOf 尋找。
function handleCheckProject() {
const checkBoxes = document.querySelectorAll("input[type='checkbox']");
const maxChecked = 3; // 最多選取數

checkBoxes.forEach((checkbox) => {
checkbox.addEventListener("change", () => {
const projectSeq = checkbox.getAttribute("data-seq");

// 判斷目前的 seq 在 checkedProjectList 中的 index,若無則返回 -1
const findCheckBoxIndex = checkedProjectList.indexOf(+projectSeq);

// 如果已達最多選取數,並且清單中不存在此 seq
if (
checkedProjectList.length === maxChecked &&
findCheckBoxIndex === -1
) {
checkbox.checked = false;
} else {
// 未達最多選取數​
findCheckBoxIndex === -1
? checkedProjectList.push(+projectSeq)
: checkedProjectList.splice(findCheckBoxIndex, 1);
}

//渲染選取專案
showCheckedList.textContent = checkedProjectList.join("、");
});
});
}
  1. 完成!


四、結尾

這是我原本發布在六角學院 2023 JS 直播班討論區分享的心得,現移植到部落格存放,謝謝 Paul 提供了改寫優化。

五、資料參考

avatar-img
16會員
23內容數
從藝術領域轉職到前端工程師,喜歡書寫學習歷程和技術文件,掌握經驗與隨時提取的感覺很好。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Ann Chou的沙龍 的其他內容
分享這套功能齊全,視覺超級可愛還有響應式變化,教學文件又很容易閱讀的套件 Evo Calendar。 當初在製作 JS side project 時,想找一個與 VCalendar 視覺相近的套件,但多半都不合意。六角學院指派的 side project 教練也幫我找了另一個純 JS 的範例,也很
不喜歡 HTML 原生的時間選擇器外型嗎?試試看 flatpickr.js 吧! 以下是使用 flatpickr 做簡單的示例,以及 flatpickr 使用注意事項。
我依舊維持著修習 JS 的步伐,但我仍然覺得自己對 JS 的熟悉度不足。在 JS 班開課後,我藉由刷題庫和做 side project 專題,填補了課前的不自信感和知識焦慮。最終我們小組在 2.5 個月內開發了一個訂閱制電商網站的前後台,我也參加了 F2E week1 完賽,獲得銀級徽章
這是我參加為期三個月的六角學院 2023 網頁前端切版直播班中的學習和成長經驗。最初參加直播班時誤以為自己已經具備足夠的前端知識,但後來發現自己的程式碼還有改進的空間。我在參與直播班的過程中,不僅學到了更多切版技術,也在小組協作中體驗到了組織能力和團隊合作的重要性,並從做設計稿與切版中發現個人優勢。
軟工體驗營最後一周,短短一個月的前端程式體驗,真的是非常超值的課程。不論是檢視自己對寫程式的感受,或是透過社群認識自己的新面向、軟實力的培養都是。六角是非常新手友善的程式學校,推薦給每位想學習前端的初心者朋友。
遭受學習挫折的一周,開始發現社群的重要性,最終還是回歸於自己報名體驗營的目的。
分享這套功能齊全,視覺超級可愛還有響應式變化,教學文件又很容易閱讀的套件 Evo Calendar。 當初在製作 JS side project 時,想找一個與 VCalendar 視覺相近的套件,但多半都不合意。六角學院指派的 side project 教練也幫我找了另一個純 JS 的範例,也很
不喜歡 HTML 原生的時間選擇器外型嗎?試試看 flatpickr.js 吧! 以下是使用 flatpickr 做簡單的示例,以及 flatpickr 使用注意事項。
我依舊維持著修習 JS 的步伐,但我仍然覺得自己對 JS 的熟悉度不足。在 JS 班開課後,我藉由刷題庫和做 side project 專題,填補了課前的不自信感和知識焦慮。最終我們小組在 2.5 個月內開發了一個訂閱制電商網站的前後台,我也參加了 F2E week1 完賽,獲得銀級徽章
這是我參加為期三個月的六角學院 2023 網頁前端切版直播班中的學習和成長經驗。最初參加直播班時誤以為自己已經具備足夠的前端知識,但後來發現自己的程式碼還有改進的空間。我在參與直播班的過程中,不僅學到了更多切版技術,也在小組協作中體驗到了組織能力和團隊合作的重要性,並從做設計稿與切版中發現個人優勢。
軟工體驗營最後一周,短短一個月的前端程式體驗,真的是非常超值的課程。不論是檢視自己對寫程式的感受,或是透過社群認識自己的新面向、軟實力的培養都是。六角是非常新手友善的程式學校,推薦給每位想學習前端的初心者朋友。
遭受學習挫折的一周,開始發現社群的重要性,最終還是回歸於自己報名體驗營的目的。
你可能也想看
Google News 追蹤
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
1 文字之後補充 2 3 4 5
https://x.com/i/web/status/1780213284769337813
Thumbnail
【Paged Notes】是一款簡單、清晰、直觀的記事軟體,提供自訂記事分類、清單記事和備忘記事等功能。同時支援標示記事重要性和自訂記事底色,並可自動連結網址、電話和E-mail。未來將持續新增相關模組與功能。
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
1 文字之後補充 2 3 4 5
https://x.com/i/web/status/1780213284769337813
Thumbnail
【Paged Notes】是一款簡單、清晰、直觀的記事軟體,提供自訂記事分類、清單記事和備忘記事等功能。同時支援標示記事重要性和自訂記事底色,並可自動連結網址、電話和E-mail。未來將持續新增相關模組與功能。