完整代码见下:
html:
<!DOCTYPE html>
<html lang="zh-CN">
<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>JS 分页</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link rel="stylesheet" href="css/index.css" />
</head>
<body>
<div class="container">
<ul class="content"></ul>
<ul class="pagination"></ul>
</div>
<script src="js/index.js"></script>
</body>
</html>
css:
* {
padding: 0;
margin: 0;
// 禁止元素被选中
user-select: none;
}
body {
// 弹性布局 让页面元素垂直+水平居中
display: flex;
justify-content: center;
align-items: center;
// 让页面占浏览器可视区域的高度
height: 100vh;
background-color: #f8f9ff;
}
ul {
// 去除小黑点
list-style: none;
}
// 定义公共颜色
@lightColor: #cdd5f7;
@darkColor: #646b8a;
@activeColor: #6b5bfd;
.container {
display: flex;
flex-direction: column;
align-items: center;
width: 600px;
// 内容列表
.content {
display: flex;
// 允许换行
flex-wrap: wrap;
li {
display: flex;
// 主轴向下
flex-direction: column;
// 两端对齐
justify-content: space-between;
align-items: center;
width: 100px;
height: 100px;
padding: 10px;
i {
display: flex;
justify-content: center;
align-items: center;
width: 70px;
height: 70px;
// 圆角
border-radius: 8px;
font-size: 30px;
color: @darkColor;
background-color: #fff;
// 鼠标移入变小手
cursor: pointer;
// 过渡
transition: all 0.3s;
// 阴影
box-shadow: 0 16px 32px rgba(90, 100, 130, 0.1);
&:hover {
color: @activeColor;
// 放大1.2倍
transform: scale(1.2);
}
}
p {
font-size: 13px;
color: @lightColor;
}
}
}
// 分页列表
.pagination {
display: flex;
align-items: center;
height: 50px;
margin: 30px;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 16px 32px rgba(90, 100, 130, 0.1);
li {
display: flex;
justify-content: center;
align-items: center;
width: 25px;
height: 25px;
margin: 10px;
font-size: 14px;
border-radius: 5px;
transition: all 0.3s;
// 左右按钮
&.page-btn {
// 默认都为不可点击态
color: @lightColor;
font-size: 22px;
&.isClick {
// 可点击态
color: @darkColor;
cursor: pointer;
&:hover {
color: #fff;
background-color: @activeColor;
}
}
}
// 数字按钮
&.page-number {
color: @darkColor;
cursor: pointer;
&:hover {
color: #fff;
// 文字加粗
font-weight: bold;
background-color: @activeColor;
}
// 选中状态
&.active {
color: #fff;
// 文字加粗
font-weight: bold;
background-color: @activeColor;
// 鼠标变为默认箭头
cursor: default;
}
}
// 省略号按钮
&.page-dot {
color: @lightColor;
font-size: 18px;
cursor: pointer;
&:hover {
color: @activeColor;
}
&::after {
content: "more_horiz";
}
// 左边
&.page-dot-prev:hover {
&::after {
content: "first_page";
}
}
// 右边
&.page-dot-next:hover {
&::after {
content: "last_page";
}
}
}
}
}
}
js:
/* icons为预先准备好的图标100个
size为每页显示的个数
page是显示的总页数 向上取整(一个也会占一页)
pagerCount为要显示的数字按钮的个数 */
const icons = [
"pregnant_woman",
"accessibility",
"accessibility_new",
"emoji_people",
"sports_kabaddi",
"sports_handball",
"elderly",
"accessible",
"accessible_forward",
"hotel",
"sports_baseball",
"sports_basketball",
"sports_football",
"sports_golf",
"sports_hockey",
"sports_motorsports",
"sports_rugby",
"sports_soccer",
"sports_tennis",
"sports_volleyball",
"pedal_bike",
"moped",
"two_wheeler",
"directions_car",
"local_taxi",
"local_shipping",
"directions_bus",
"directions_transit",
"subway",
"flight",
"mouse",
"keyboard",
"headset",
"desktop_mac",
"sim_card",
"phone_android",
"phone_iphone",
"watch",
"videogame_asset",
"scanner",
"attach_email",
"attachment",
"cloud_circle",
"cloud_download",
"cloud_upload",
"text_snippet",
"request_quote",
"folder",
"topic",
"folder_shared",
"4k",
"fiber_new",
"fiber_dvr",
"explicit",
"closed_caption",
"hd",
"high_quality",
"closed_caption_disabled",
"score",
"picture_as_pdf",
"filter_1",
"filter_2",
"filter_3",
"filter_4",
"filter_5",
"filter_6",
"filter_7",
"filter_8",
"filter_9",
"filter_9_plus",
"looks_one",
"looks_two",
"looks_3",
"looks_4",
"looks_5",
"exposure_neg_1",
"exposure_neg_2",
"exposure_zero",
"exposure_plus_1",
"exposure_plus_2",
"camera_alt",
"center_focus_strong",
"camera",
"camera_roll",
"photo",
"movie_creation",
"motion_photos_on",
"motion_photos_paused",
"local_movies",
"movie_filter",
"arrow_forward",
"arrow_back",
"subdirectory_arrow_right",
"subdirectory_arrow_left",
"arrow_downward",
"arrow_upward",
"north_east",
"north_west",
"south_east",
"south_west",
],
size = 10,
page = Math.ceil(icons.length / size),
pagerCount = 5;
// 当前选中的页数
let current = 1;
// 显示内容列表
const _content = document.querySelector(".content");
const showContent = () => {
// 每次遍历新内容 首先清空
_content.innerHTML = "";
icons.forEach((item, index) => {
// 遍历计算方法 当前为第1页 一页10个 第一页的数据就是 0 - 10(不包含) 第二页为 10 - 20(不包含) 以此类推
if (index >= (current - 1) * size && index < current * size) {
// 每遍历一个创建一个li元素
const li = document.createElement("li");
// li元素添加内容
li.innerHTML = `
<i class="material-icons">${item}</i>
<p>${index + 1}</p>`;
// 添加到列表元素中
_content.appendChild(li);
}
});
};
// 创建分页列表
const _pagination = document.querySelector(".pagination");
const createPagination = () => {
showContent();
// 刚开始就要有左按钮
// 当前页数不为1就为可点击态
let lis = `
<li class="material-icons page-btn page-btn-prev ${
current !== 1 ? "isClick" : ""
}">
keyboard_arrow_left
</li>`;
if (current < 1 || current > page) {
throw `current 参数最小值为1 最大值为${page}`;
// 当当前页数小于1或者大于总页数了就抛出错误
} else if (pagerCount < 5) {
throw "pagerCount 参数最小值为5";
// 小于5 分页无意义
} else if (page <= pagerCount) {
// 如果总页数小于了要显示的数字按钮个数 就直接遍历了 不需要显示省略按钮
for (let i = 1; i <= page; i++) {
lis += `<li class="page-number ${
i == current ? "active" : ""
}">${i}</li>`;
}
} else {
// 定义两个参数
// 用来保存当前选中分页前后的显示数字按钮(不包括省略前后的和选中的) 刚好是以下计算方法
// 有问题 pagerCount 为偶数 显示小数点 将beforeNumber向下取整就可以了
let beforeNumber = Math.floor(current - (pagerCount - 3) / 2),
afterNumber = current + (pagerCount - 3) / 2;
// 显示左省略按钮
if (current >= pagerCount - 1) {
lis += `<li class="page-number">1</li>
<li class="material-icons page-dot page-dot-prev"></li>`;
}
// 提出问题: 选中页数为1 显示了0
// 解决 当页数为1 将beforeNumber改为1 afterNumber为除去省略号后面的一个按钮
// 同理解决current == page
// 又有问题 点击前三个应该不分页 到 4(针对pagerCount参数来说) 了该分页 同理求得current == page
if (current >= 1 && current < pagerCount - 1) {
beforeNumber = 1;
afterNumber = pagerCount - 1;
} else if (current <= page && current > page - (pagerCount - 2)) {
beforeNumber = page - (pagerCount - 2);
afterNumber = page;
}
for (let i = beforeNumber; i <= afterNumber; i++) {
lis += `<li class="page-number ${
i == current ? "active" : ""
}">${i}</li>`;
}
}
// 显示右省略按钮
if (current <= page - (pagerCount - 2)) {
lis += `
<li class="material-icons page-dot page-dot-next"></li>
<li class="page-number">${page}</li>`;
}
// 最后拼接右按钮
// 当前页数不是总页数就为可点击态
lis += `
<li class="material-icons page-btn page-btn-next ${
current !== page ? "isClick" : ""
}">
keyboard_arrow_right
</li>`;
_pagination.innerHTML = lis;
// OK 分页已经没问题了 改变参数均没问题 随意修改
// 点击数字按钮
const _pageNumbers = document.querySelectorAll(".page-number");
_pageNumbers.forEach((item) => {
item.addEventListener("click", () => {
// item.innerHTML为字符串 需要转为数字
current = parseInt(item.innerHTML);
createPagination();
});
});
// 下一页
const _pageBtnNext = document.querySelector(".page-btn-next");
_pageBtnNext.addEventListener("click", () => {
if (current !== page) {
current++;
createPagination();
}
});
// 上一页
const _pageBtnPrev = document.querySelector(".page-btn-prev");
_pageBtnPrev.addEventListener("click", () => {
if (current !== 1) {
current--;
createPagination();
}
});
// 前进 pagerCount - 2 格
const _pageDotNext = document.querySelector(".page-dot-next");
// 因为省略按钮会时隐时现 直接绑定会报找不到元素错误
// ?. 就可以了 只有元素存在再去绑定后面的事件
_pageDotNext?.addEventListener("click", () => {
current += pagerCount - 2;
createPagination();
});
// 后退 pagerCount - 2 格
const _pageDotPrev = document.querySelector(".page-dot-prev");
_pageDotPrev?.addEventListener("click", () => {
current -= pagerCount - 2;
createPagination();
});
};
createPagination();
但这个写法也有一个问题,每次翻页都要使用遍历去生成dom元素,这样比较耗费性能,而且遍历本身的性能也不高,如果是分页同时还要去调异步接口,使用遍历的方式显然会有问题。