实战项目:AJAX实现无刷新分页功能
一、效果图
二、实现步骤
首先,实现一个分页,PHP后端需要给前端提供两个数据即可实现分页:
1. 一共需要分多少页;
2. 每页显示的数据是多少条。
根据上面两条原理,我们先设计一个数据库,并插入一些数据:
- 建表语句:
CREATE TABLE `staffs` (
`uid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '姓名',
`age` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '年龄',
`gender` enum('male','female') COLLATE utf8mb4_unicode_ci NOT NULL,
`salary` int(10) unsigned NOT NULL DEFAULT '2000',
`email` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮箱',
`birthday` date NOT NULL COMMENT '生日',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改日期',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态',
PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
- 插入数据:
INSERT INTO `staffs` VALUES (1, '残破的蛋蛋', 33, 'male', 4100, 'admin@admin.cn', '1990-03-31', '2021-02-25 19:20:09', '2021-03-02 15:13:30', 0);
INSERT INTO `staffs` VALUES (2, '拤碎的蛋蛋', 0, 'female', 2500, 'sui@admin.cn', '1991-04-26', '2021-02-25 19:20:09', '2021-03-02 14:48:49', 1);
INSERT INTO `staffs` VALUES (3, 'king', 0, 'male', 6500, 'king@php.cn', '1992-10-29', '2021-02-25 19:20:09', '2021-03-02 14:48:49', 1);
INSERT INTO `staffs` VALUES (4, 'amy', 0, 'female', 7800, 'amy@163.com', '1998-10-22', '2021-02-25 19:20:09', '2021-03-02 14:48:49', 1);
INSERT INTO `staffs` VALUES (5, 'betty', 0, 'female', 9800, 'betty@qq.com', '1953-10-19', '2021-02-25 19:20:09', '2021-03-02 14:48:49', 1);
INSERT INTO `staffs` VALUES (6, 'jack', 24, 'male', 12500, 'jack@php.cn', '1977-10-24', '2021-02-25 19:20:09', '2021-03-02 14:48:49', 1);
INSERT INTO `staffs` VALUES (7, 'marry', 12, 'female', 15800, 'marry@php.cn', '1990-01-08', '2021-02-25 19:20:09', '2021-03-02 14:47:37', 1);
INSERT INTO `staffs` VALUES (8, 'alice', 0, 'female', 8600, 'alice@php.cn', '1989-09-18', '2021-02-25 19:20:09', '2021-03-01 17:09:08', 1);
INSERT INTO `staffs` VALUES (9, 'admin', 0, 'male', 16600, 'admin@php.cn', '1989-09-18', '2021-02-25 19:20:09', '2021-03-01 17:09:08', 1);
INSERT INTO `staffs` VALUES (10, 'lisa', 0, 'female', 13500, 'lisa@qq.com', '1983-09-13', '2021-02-25 19:20:09', '2021-03-01 17:09:08', 1);
INSERT INTO `staffs` VALUES (11, 'peter', 32, 'male', 9600, 'peter@163.com', '1993-09-29', '2021-02-25 19:20:09', '2021-03-01 18:14:30', 1);
INSERT INTO `staffs` VALUES (12, 'linda', 0, 'female', 5600, 'linda@163.com', '1993-09-29', '2021-02-25 19:20:09', '2021-03-01 17:09:08', 1);
INSERT INTO `staffs` VALUES (13, 'Jerry', 30, 'male', 8899, '12345@qq.com', '1991-08-01', '2021-02-27 00:04:00', '2021-03-01 17:09:08', 1);
INSERT INTO `staffs` VALUES (14, 'Tom', 30, 'female', 27891, 'tom@qq.com', '1996-01-01', '2021-02-27 00:08:47', '2021-03-01 17:18:31', 1);
INSERT INTO `staffs` VALUES (15, 'Kimi', 35, 'male', 19871, 'Kimi@126.com', '2000-01-27', '2021-02-27 00:11:08', '2021-02-27 00:11:08', 1);
INSERT INTO `staffs` VALUES (16, '李磊', 35, 'male', 6871, 'Lilei@163.com', '1993-06-17', '2021-02-27 00:41:13', '2021-02-27 00:41:13', 1);
- 效果图:
三、代码实现
3.1 目录结构
下面是我在写这个分页的时候创建的目录结构:
3.2 书写代码
因为要使用到数据库,首先需要做的是数据库配置:
- config.php 数据库配置文件
<?php
// 数据库配置
return [
'type' => 'mysql',
'host' => 'localhost',
'dbname'=> 'phpedu',
'port' => 3306,
'charset'=> 'utf8mb4',
'username' => 'root',
'password' => 'root',
];
数据库配置完毕之后,连接数据库,创建PDO
对象。
- connect.php 连接数据库文件
$config = require __DIR__ . '/config.php';
extract($config);
$dsn = sprintf('%s:host=%s;dbname=%s;port=%s;charset=%s', $type, $host, $dbname, $port, $charset);
try {
$pdo = new PDO($dsn, $username, $password);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die("数据库连接失败:" . $e->getMessage());
}
以上工作完成之后,我们写一下index.php页面,这是最开始的入口页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>分页案例</title>
</title>
<link rel="stylesheet" href="./paginate/static/css/table.css">
<script src="./paginate/static/js/table.js"></script>
</head>
<body>
<div class="container">
<div class="box">
<table cellpadding="0" cellspacing="0">
<caption>员工信息管理系统</caption>
<thead>
<tr>
<td>ID</td>
<td>名字</td>
<td>年龄</td>
<td>性别</td>
<td>工资</td>
<td>邮箱</td>
<td>生日</td>
<td>操作</td>
</tr>
</thead>
<tbody class="tableBody">
</tbody>
</table>
<div class="paginate">
</div>
</div>
</div>
</body>
</html>
<script>
// 加载默认数据
getList(1);
</script>
由于数据都是通过AJAX异步请求获取的,这里引用了paginate/static/js/table.js
里面的方法。
- table.js 脚本代码
/**
* 获取当前页的数据
* @param {int} val 当前页码值
*/
function getList(val) {
// 1. 创建xhr对象
let xhr = new XMLHttpRequest;
console.log(xhr);
// 2. 配置xhr参数
xhr.open('GET', `http://localhost:8888/PHP/20210225/paginate/getList.php?p=${val}`);
xhr.responseType = 'json';
// 3. 处理xhr响应
// 成功
xhr.onload = () => {
console.log(xhr.response);
// console.log(createTable(xhr.response));
// 内容
createTable(xhr.response);
// 分页
createPaginate(xhr.response);
};
// 失败
xhr.onerror = () => {
console.log('xhr Falied...');
}
// 4. 发送请求
// let formData = new FormData();
// formData.append("p", val);
xhr.send(null);
}
/**
* 创建员工信息表
* @param {object} table 后端返回的员工数据
*/
function createTable(table) {
// 创建表格html结构
let tableHTML = '<tr>';
for (const item of table.staffs) {
console.log(item);
let {uid, name, age, gender, salary, email, birthday} = item;
tableHTML += `<td>${uid}</td>`;
tableHTML += `<td>${name}</td>`;
tableHTML += `<td>${age}</td>`;
tableHTML += `<td>${gender}</td>`;
tableHTML += `<td>${salary}</td>`;
tableHTML += `<td>${email}</td>`;
tableHTML += `<td>${birthday}</td>`;
tableHTML += `<td>
<div class="btn-wrap">
<a data-uid=${uid} class="btn edit">编辑</a>
<a data-uid=${uid} class="btn del">删除</a>
</div>
</td>`;
tableHTML += '</tr><tr>';
}
tableBody.innerHTML = tableHTML;
// 编辑按钮
const edit = document.querySelectorAll('.edit');
Array.from(edit).forEach(item => item.addEventListener('click', ev => {
ev.preventDefault();
console.log(ev.target.dataset.uid);
let uid = ev.target.dataset.uid;
// let queryString = location.search;
window.location.href = `./paginate/edit.php?p=${table.page}&uid=${uid}`;
}));
// 删除按钮
const del = document.querySelectorAll('.del');
Array.from(del).forEach(item => item.addEventListener('click', ev => {
ev.preventDefault();
console.log(ev.target.dataset.uid);
let uid = ev.target.dataset.uid;
let url = `./paginate/api.php?actions=del&uid=${uid}`;
if (confirm('确定删除该员工数据吗?')) {
let xhr = new XMLHttpRequest();
console.log(xhr);
xhr.open('get', url);
xhr.responseType = 'json';
xhr.onload = () => {
console.log(xhr.response);
if (xhr.response.status === 1) {
alert(xhr.response.msg);
window.location.href = 'index.php';
} else {
alert(xhr.response.msg);
}
};
xhr.send(null);
};
}));
}
/**
* 创建分页数字按钮
* @param {object} obj 员工信息数据
*/
function createPaginate(obj) {
// 获取总页数
let pages = obj.pages;
// 获取当前页
let page = obj.page;
console.log(pages, page);
// 上一页
let prev = page - 1;
let pageBtn = '';
if (page == 1) {
prev = page;
}
// 上一页、首页
pageBtn += `<a href="javascript:;" data-page="${prev}" class="prev"><</a>
<a href="javascript:;" data-page="1" class="first">首页</a>`;
// 页码
for (let i = 1; i <= pages; i++) {
pageBtn += `<a href="javascript:;" data-page="${i}" class="${i == page ? 'active' : ''}">${i}</a>`;
}
// 下一页、尾页
let next = page + 1;
if (next >= pages) {
next = pages;
}
pageBtn += `<a href="" data-page="${pages}" class="first">尾页</a>
<a href="" data-page="${next}" class="next">></a>`;
paginate.innerHTML = pageBtn;
const aBtn = document.querySelectorAll('.paginate a');
Array.from(aBtn).forEach(item => item.addEventListener('click', ev => {
ev.preventDefault();
getList(ev.target.dataset.page);
}));
}
- css样式
下面是页面的css样式:
- table.css
.container {
text-align: center;
width: 100%;
text-align: center;
}
.box {
display: inline-block;
}
table {
border-left: 1px solid #e6e6e6;
border-top: 1px solid #e6e6e6;
color: #666;
font-size: 14px;
margin: 10px auto 0;
}
table caption {
font-size: 1.5rem;
margin: .5em;
}
td {
border: 1px solid #e6e6e6;
border-top: none;
border-left: none;
text-align: center;
padding: 0 15px;
height: 38px;
}
thead tr {
background-color: #f2f2f2;
}
thead tr td {
font-weight: 400;
}
tbody tr {
border-left: 1px;
}
/* btn */
.btn {
padding: 0 5px;
height: 22px;
line-height: 22px;
background-color: #009688;
border-radius: 2px;
cursor: pointer;
text-decoration: none;
color: #fff;
font-size: 12px;
display: inline-block;
}
.del {
background-color: #FF5722;
}
.paginate {
padding: 7px 7px 0;
margin: 0 auto;
text-align: left;
}
.paginate a {
height: 26px;
line-height: 26px;
padding: 0 12px;
color: #333;
font-size: 12px;
text-decoration: none;
display: inline-block;
}
.paginate a.active {
background-color: #009688;
border-radius: 2px;
color: #fff;
}
- dialog.css (编辑界面的样式)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
position: relative;
}
header, footer {
height: 8vh;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 0.5em;
background-color: #000;
color: #ffffff;
}
/* 按钮 */
button {
width: 10em;
height: 2em;
line-height: 2em;
text-align: center;
color: #fff;
font-size: 0.8em;
border: none;
background-color: #1881ec;
border-radius: 2px;
cursor: pointer;
transition: all 0.3s;
}
button:hover {
cursor: pointer;
opacity: 0.8;
}
/* 模态框 */
.modal {
height: calc(84vh - 1em);
margin: 0.5em 0;
}
/* 模态框背景 */
.modal .modal-bg {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: #000;
opacity: 0.3;
z-index: 98;
}
.modal .modal-group {
position: fixed;
width: 28em;
height: 23em;
left: 50%;
margin-left: -14em;
margin-top: 5em;
background-color: #666;
z-index: 99;
}
.modal .modal-group .hd-wrap {
margin: 1em;
height: 2em;
}
.modal .modal-group .bd-wrap {
padding: 0 1em 1em
}
.modal .modal-group .bd-wrap .box {
display: flex;
height: calc(20em - 5em);
flex-flow: column nowrap;
}
.modal .modal-group .bd-wrap .box .input {
height: 2em;
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 1em;
}
.modal .modal-group .bd-wrap .box .input label {
width: 4em;
text-align: left;
}
.modal .modal-group .bd-wrap .box .input input
{
width: calc(100% - 4em);
height: 2em;
line-height: 2em;
border: 1px solid #e6e6e6;
padding: 0 10px;
font-size: 14px;
border-radius: 2px;
}
.modal .modal-group .bd-wrap .box .input input.captcha {
width: 30%;
}
/* 登录按钮 */
.btn-wrap {
display: flex;
align-items: center;
justify-content: center;
}
.btn-wrap button {
width: 12em;
}
.btn-wrap button.cancel-button {
margin-left: 1em;
}
.btn-wrap button.submit-button {
background-color: #009688;
}
.modal .modal-group .bd-wrap .box .submit-button{
width: 100%;
}
- 效果图:
- getList.php代码
<?php
// 引入数据库连接
require 'database/connect.php';
// 每页数量
$num = 5;
// 当前页数,该值是从客户端传递过来的,默认是当前页
$page = $_GET['p'] ?? 1;
// 计算当前页的起始偏移量
$offset = ($page - 1) * $num;
// 查询数据总页数
$sql = "SELECT CEIL(COUNT(1)/{$num}) AS total FROM `staffs` WHERE `status` = 1";
// 生成预处理对象
$stmt = $pdo->prepare($sql);
$stmt->execute();
// $stmt->debugDumpParams();
// 获取分页的总页数
$pages = $stmt->fetch()['total'];
// 每页显示的数据
$sql = "SELECT * FROM `staffs` WHERE `status` = 1 LIMIT {$offset}, {$num}";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$staffs = $stmt->fetchAll();
echo json_encode(["pages" => $pages, "page" => $page, "staffs" => $staffs]);
- edit.php代码
<?php
require __DIR__ . '/database/connect.php';
// 根据id获取员工信息
$sql = "SELECT * FROM `staffs` WHERE `uid` = {$_GET['uid']}";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$staff = $stmt->fetch();
// print_r($staff);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改员工信息</title>
<link rel="stylesheet" href="./static/css/dialog.css">
<script src="./static/js/table.js"></script>
</head>
<body>
<div class="modal">
<div class="modal-group">
<form name="form">
<div class="hd-wrap">
<h2>员工信息修改</h2>
<hr>
</div>
<div class="bd-wrap">
<div class="box login-box">
<div class="input input-box">
<label for="username">姓名</label><input id="username" class="username" name="name"
type="text" value="<?=$staff['name']?>" placeholder="请输入姓名" autofocus="" autocomplete="off">
</div>
<div class="input input-box">
<label for="password">年龄</label><input id="age" class="age" name="age" type="text"
value="<?=$staff['age']?>" placeholder="请输入年龄" autofocus="" autocomplete="off">
</div>
<div class="input input-box">
<label for="gender">性别</label><input id="gender" class="gender" name="gender"
value="<?=$staff['gender']?>" type="text" placeholder="请输入性别" autofocus="" autocomplete="off">
</div>
<div class="input input-box">
<label for="salary">工资</label><input id="salary" class="salary" name="salary"
value="<?=$staff['salary']?>" type="text" placeholder="请输入验证码" autofocus="" autocomplete="off">
</div>
<div class="input input-box">
<label for="email">邮箱</label><input id="email" class="email" name="email"
value="<?=$staff['email']?>" type="text" placeholder="请输入密码" autofocus="" autocomplete="off">
</div>
<div class="input input-box">
<label for="birthday">生日</label><input id="birthday" class="birthday" name="birthday"
value="<?=$staff['birthday']?>" type="date" placeholder="请填写出生日期" autofocus="" autocomplete="off">
</div>
<button class="submit-button" type="button">提交</button>
</div>
</div>
</form>
</div>
</div>
</body>
</html>
<script>
const submit = document.querySelector('.submit-button');
const form = document.forms.form;
const tableBody = document.querySelector('.tableBody');
// console.log(submit);
submit.onclick = ev => {
// 获取当前url地址 ? 后面的值
const url = location.search;
console.log(url);
let strs = '';
if (url.indexOf("?") !== -1) {
let str = url.substr(1);
strs = str.split('&');
console.log(strs);
}
let p = strs[0].split('=')[1];
let uid = strs[1].split('=')[1];
// console.log(p, uid);
// 1. 创建xhr对对象
let xhr = new XMLHttpRequest;
// 2. 配置xhr
// http://localhost:8888/PHP/20210225/paginate/api.php?actions=edit
xhr.open('post', `api.php?actions=update&p=${p}&uid=${uid}`);
xhr.responseType = 'json';
// 3. 处理xhr响应
// 成功
xhr.onload = () => {
console.log(xhr.response);
if(xhr.response.status === 1) {
alert(xhr.response.msg);
location.href="../index.php";
}
};
// 失败
xhr.onerror = () => {
tableBody.innerHTML = 'Error';
};
// 4. 发送xhr请求
xhr.send(new FormData(form));
};
</script>
- api.php代码
<?php
// 引入数据库连接
require __DIR__ . '/database/connect.php';
$actions = $_GET['actions'];
// 将获取的id转换成整型
$uid = intval($_GET['uid']);
switch ($actions) {
case 'update':
$sql = <<< SQL
UPDATE staffs
SET `name`=:name, `age`=:age, `gender`=:gender, `salary`=:salary, `email`=:email, `birthday`=:birthday
WHERE uid = {$uid};
SQL;
$stmt = $pdo->prepare($sql);
$stmt->execute($_POST);
// $stmt->debugDumpParams();
if ($stmt->rowCount() == 1) {
echo json_encode(['status' => 1, 'msg' => '更新成功,正在跳转...']);
// echo '<script>alert(\'修改成功\');window.location.href="../index.php?p='.$_GET['p'].'"</script>';
} else {
echo json_encode(['status' => 0, 'msg' => '更新失败,请重试...']);
}
break;
case 'del':
$sql = "UPDATE `staffs` SET `status` = 0 WHERE `uid` = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$uid]);
if ($stmt->rowCount() == 1) {
echo json_encode(['status' => 1, 'msg' => '删除成功,正在跳转...']);
} else {
echo json_encode(['status' => 0, 'msg' => '删除失败,请重试...']);
}
break;
default:
die('不合法的操作!');
}
项目线上体验地址:AJAX无刷新分页