[前端 jQuery] --Ajax POST {pageIndex, pageSize}→ [後端 SupplyAJAX.aspx/ASHX] → [SQL 分頁查詢] → [JSON 序列化] → [前端渲染 Table + 分頁控件]
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="AjaxPaging.aspx.cs" Inherits="Demo.AjaxPaging" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Ajax 無刷新分頁</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-pagination@1.0.1/jquery.simplePagination.js"></script>
<link href="https://cdn.jsdelivr.net/npm/simple-pagination@1.0.1/simplePagination.css" rel="stylesheet" />
<style>
.table-responsive { margin-top: 20px; }
#pagination { margin: 20px 0; text-align: center; }
</style>
</head>
<body>
<div class="container">
<h3>學生名單 (Ajax 分頁)</h3>
<div class="table-responsive">
<table class="table table-striped table-hover" id="tblStudents">
<thead class="table-dark">
<tr><th>ID</th><th>姓名</th><th>性別</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<div id="pagination"></div>
</div>
<script>
const PAGE_SIZE = 10;
let totalRecords = 0;
$(function () {
loadPage(1); // 首次載入第 1 頁
});
function loadPage(pageIndex) {
$.ajax({
url: 'SupplyAJAX.ashx', // 或 .aspx
type: 'POST',
dataType: 'json',
data: {
action: 'list',
pageIndex: pageIndex,
pageSize: PAGE_SIZE,
_: Date.now() // 防快取
},
beforeSend: () => $('#tblStudents tbody').html('載入中... '),
success: function (resp) {
if (resp.success) {
renderTable(resp.data);
totalRecords = resp.total;
initPagination(pageIndex);
} else {
showError(resp.message);
}
},
error: function (xhr, status, err) {
showError('請求失敗:' + err);
}
});
}
function renderTable(list) {
var html = '';
$.each(list, function (i, item) {
html += `${item.Id} ${item.Name} ${item.Gender} `;
});
$('#tblStudents tbody').html(html);
}
function initPagination(currentPage) {
$('#pagination').pagination({
items: totalRecords,
itemsOnPage: PAGE_SIZE,
currentPage: currentPage,
displayedPages: 3,
edges: 1,
prevText: '上一頁',
nextText: '下一頁',
onPageClick: function (page, evt) {
evt.preventDefault();
loadPage(page);
}
});
}
function showError(msg) {
$('#tblStudents tbody').html(`${msg} `);
$('#pagination').empty();
}
</script>
</body>
</html>
<%@ WebHandler Language="C#" Class="SupplyAJAX" %>
using System;
using System.Collections.Generic;
using System.Data;
using System.Web;
using System.Web.Script.Serialization;
using System.Text;
public class SupplyAJAX : IHttpHandler
{
private static readonly JavaScriptSerializer Json = new JavaScriptSerializer();
private static readonly List _allStudents = GenerateMockData(1000); // 模擬 1000 筆
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json; charset=utf-8";
string action = context.Request["action"];
try
{
switch (action)
{
case "list":
HandleList(context);
break;
case "count":
context.Response.Write(Json.Serialize(new { success = true, total = _allStudents.Count }));
break;
default:
WriteError("未知 action");
break;
}
}
catch (Exception ex)
{
WriteError("伺服器錯誤:" + ex.Message);
}
}
private void HandleList(HttpContext context)
{
int pageIndex = int.Parse(context.Request["pageIndex"] ?? "1");
int pageSize = int.Parse(context.Request["pageSize"] ?? "10");
if (pageIndex < 1) pageIndex = 1;
// 記憶體分頁 (實務換成 SQL: OFFSET/FETCH 或 ROW_NUMBER)
var paged = _allStudents
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToList();
var result = new
{
success = true,
total = _allStudents.Count,
data = paged
};
context.Response.Write(Json.Serialize(result));
}
private void WriteError(string msg)
{
context.Response.Write(Json.Serialize(new { success = false, message = msg }));
}
public bool IsReusable => false;
// 模擬資料
private static List GenerateMockData(int count)
{
var list = new List(count);
for (int i = 1; i <= count; i++)
list.Add(new Student { Id = i, Name = $"學生{i:D4}", Gender = i % 2 == 0 ? "女" : "男" });
return list;
}
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
}
| 環節 | 重點 |
|---|---|
| 分頁參數 | 前端傳 `pageIndex` (1-based) + `pageSize`,後端 `Skip/Take` 或 SQL `OFFSET/FETCH` |
| 總筆數 | 首次載入或每次回傳 `total`,分頁控件需知總數計算頁碼 |
| JSON 格式 | `{ success: true, total: 1000, data: [...] }` 統一封裝,前端統一錯誤處理 |
| 防快取 | 查詢字串加 `_=Date.now()` 或 `cache: false` |
| 序列化 | `JavaScriptSerializer` (內建) 或 `Newtonsoft.Json` (效能更好、支援 DateTime 格式化) |
| 分頁控件 | `simplePagination` / `twbsPagination` / `laypage` / 自寫,核心是 `onPageClick` 觸發 `loadPage` |
| 載入狀態 | `beforeSend` 顯示「載入中...」,避免使用者重複點擊 |
-- SQL Server 2012+ 標準語法
SELECT Id, Name, Gender
FROM Students
ORDER BY Id
OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY;
-- 取得總數 (可用 COUNT(*) OVER() 窗口函數單查詢完成,或分兩次查)
SELECT COUNT(*) FROM Students;
配合 Dapper / EF Core:
var sql = @"SELECT * FROM Students ORDER BY Id OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY;
SELECT COUNT(*) FROM Students;";
using (var multi = conn.QueryMultiple(sql, new { Offset = (pageIndex-1)*pageSize, PageSize = pageSize }))
{
var data = multi.Read().ToList();
var total = multi.ReadSingle();
return new { success = true, total, data };
}
// Controller
[HttpPost]
public IActionResult List(int pageIndex = 1, int pageSize = 10)
{
var (data, total) = _service.GetPaged(pageIndex, pageSize);
return Json(new { success = true, total, data });
}
// 前端 (Razor 頁面)
...
jQuery Ajax + JSON 無刷新分頁是 WebForms 時代提升體驗的經典模式。核心在於:統一 JSON 封裝、前端分頁控件與後端分頁邏輯對齊、防快取、載入狀態、錯誤統一處理。現代專案請直接使用 ASP.NET Core Web API + fetch/axios + 前端框架 (Vue/React/Blazor),獲得型別安全、組件化、測試友善的開發體驗。