傳統分頁每次跳轉需重新載入整頁,體驗差。本文實作一個 PHP 分頁類 + AJAX 前端無刷新加載 的完整方案。
每頁 7 條 第 1 頁:LIMIT 0, 7 第 2 頁:LIMIT 7, 7 第 3 頁:LIMIT 14, 7 總頁數 = ceil(總條數 / 每頁條數)
Pagination'條記錄','prev'=>'上一頁','next'=>'下一頁','first'=>'首頁','last'=>'尾頁'];
private $listNum = 8; // 頁碼顯示數量
public function __construct($total, $listRows = 10, $extraParams = '') {
$this->total = $total;
$this->listRows = $listRows;
$this->uri = $this->buildUri($extraParams);
$this->page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
$this->pageNum = ceil($this->total / $this->listRows);
$this->limit = "LIMIT " . ($this->page - 1) * $this->listRows . ", {$this->listRows}";
}
private function buildUri($extra) {
$url = $_SERVER['REQUEST_URI'] . (strpos($_SERVER['REQUEST_URI'], '?') ? '' : '?') . $extra;
$parts = parse_url($url);
if (isset($parts['query'])) {
parse_str($parts['query'], $params);
unset($params['page']);
return $parts['path'] . '?' . http_build_query($params);
}
return $parts['path'] . '?';
}
public function __get($name) { return $name === 'limit' ? $this->limit : null; }
private function start() { return $this->total ? ($this->page - 1) * $this->listRows + 1 : 0; }
private function end() { return min($this->page * $this->listRows, $this->total); }
private function link($page, $text) {
return "uri}&page={$page}')\">{$text}";
}
private function span($text) { return "{$text}"; }
private function first() { return $this->page == 1 ? $this->span($this->config['first']) : $this->link(1, $this->config['first']); }
private function prev() { return $this->page == 1 ? $this->span($this->config['prev']) : $this->link($this->page - 1, $this->config['prev']); }
private function next() { return $this->page == $this->pageNum ? $this->span($this->config['next']) : $this->link($this->page + 1, $this->config['next']); }
private function last() { return $this->page == $this->pageNum ? $this->span($this->config['last']) : $this->link($this->pageNum, $this->config['last']); }
private function pageList() {
$html = ''; $half = floor($this->listNum / 2);
for ($i = $half; $i >= 1; $i--) { $p = $this->page - $i; if ($p >= 1) $html .= $this->link($p, $p); }
$html .= $this->span($this->page);
for ($i = 1; $i <= $half; $i++) { $p = $this->page + $i; if ($p <= $this->pageNum) $html .= $this->link($p, $p); else break; }
return $html;
}
private function goPage() {
return '
';
}
public function fpage(array $display = [0,1,2,3,4,5,6,7,8]) {
$html = [
" 共{$this->total}{$this->config['header']} ",
" 每頁".($this->end()-$this->start()+1)."條,本頁{$this->start()}-{$this->end()}條 ",
" {$this->page}/{$this->pageNum}頁 ",
$this->first(), $this->prev(), $this->pageList(), $this->next(), $this->last(), $this->goPage()
];
$out = '';
foreach ($display as $i) $out .= $html[$i];
return $out;
}
}
data.php(AJAX 端點)query("SELECT COUNT(*) FROM goods")->fetchColumn();
$pageObj = new Pagination($total, 7);
$stmt = $pdo->query("SELECT * FROM goods {$pageObj->limit}");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 輸出表格行
foreach ($rows as $i => $r) {
echo "".($pageObj->start()+$i)." {$r['name']} {$r['number']} {$r['price']} ".date('Y-m-d H:i:s', $r['create_time'])." ";
}
// 輸出分頁導航(放在表格最後一行或獨立容器)
echo "" . $pageObj->fpage([3,4,5,6,7,8]) . " ";
?>
index.htmlAJAX 無刷新分頁 商品列表
<tr>...),前端直接 innerHTML 替換onclick="showPage(...)" 而非 href,避免跳轉parse_str + unset($params['page']) 保持其他查詢條件fetch API 替代 XMLHttpRequest,代碼更簡潔IntersectionObserver 監聽底部自動載入下一頁history.pushState 讓分頁可分享、可後退