【前端项目——分页器】手写分页器实现(JS / React)

组件介绍

用了两种方式实现,注释详细~
可能代码写的不够简洁,见谅🙁
在这里插入图片描述
1. 包含内容显示的分页器
网上看了很多实现,很多只有分页器部分,没和内容显示联动。
因此我增加了模拟content的显示,这里模拟了32条数据,通过分页器控制每页的显示。
2. 切换每页显示数目
列举了三种,5,10,20条每页
在这里插入图片描述

3. 输入值后通过enter跳转
做了一个范围保护,如果超出当前范围,会自动变成最大或最小的页码。(做提示也可以,但我不想点提示的确认)
在这里插入图片描述

原生JS实现

1. HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div class="content">
    </div>
    <div class="page">
        <button class="prev disabled">&lt;&lt;</button>
        <button class="prev disabled">&lt;</button>

        <ul class="page-list">
        </ul>

        <button class="next">&gt;</button>
        <button class="next">&gt;&gt;</button>

        <select class="page-size">
            <option value="5">每页显示5条</option>
            <option value="10">每页显示10条</option>
            <option value="20">每页显示20条</option>
        </select>

        <span>跳转到</span>
        <input class="jump-to" type="number" value="1">
        <span></span>
    </div>
    <script src="script.js"></script>
</body>

</html>

2.CSS

* {
    font-size: 16px;
    margin: 0;
    padding: 0;
}

a {
    display: inline-block;
    text-decoration: none;
    color: #000;
    padding: 10px 15px;
}

.content {
    display: flex;
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    padding: 20px;
}

.content div {
    width: 220px;
    height: 50px;
    background-color: #78b5e7;
    margin: 10px;
}

.page {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 10px;
    width: 100%;
    height: 100px;
    background-color: #f5f5f5;
}

.page-list {
    list-style: none;
    display: flex;
    flex-direction: row;
}

.prev,
.next,
.page-num {
    border-radius: 4px;
    padding: 0;
    border: 0;
    cursor: pointer;
}

.prev,
.next {
    padding: 10px 15px;
}

.prev:hover,
.next:hover,
.page-num:hover {
    background-color: #e9e7e7;
}

.active {
    background-color: #bee5fc;
}

select {
    font-size: 16px;
    padding: 5px 5px;
    border-radius: 4px;
    border: 1px solid #ccc;
}

.jump-to {
    font-size: 16px;
    padding: 5px 5px;
    border-radius: 4px;
    width: 35px;
    border: 1px solid #ccc;
}

button:active {
    background-color: #fff;
}

.disabled {
    color: #b3b3b3;
    cursor: not-allowed;
    opacity: 0.6;
}

3. JS

let content = document.querySelector('.content');
let contentNum = 32;
for (let i = 0; i < contentNum; i++) {
    let div = document.createElement('div');
    div.innerHTML = i + 1;
    content.appendChild(div);
}

let current = 1; //当前页码,默认是第一页

let pagelist = document.querySelector('.page-list');

let showpage = document.querySelector('.page-size');
let pagenum = showpage.value; //默认一页显示多少内容
let num = Math.ceil(contentNum / parseInt(pagenum)); //默认显示的页数的页数

let prev = document.querySelectorAll('.prev');
let next = document.querySelectorAll('.next');


// 展示当前页码的内容
function showContent() {
    let start = (current - 1) * parseInt(pagenum);
    let end = start + parseInt(pagenum);
    for (let i = 0; i < content.children.length; i++) {
        if (i >= start && i < end) {
            content.children[i].style.display = 'block';
        } else {
            content.children[i].style.display = 'none';
        }
    }
}
// 展示页码列表
function showPage() {
    // 清空原有的超出所需页面数量的页码列表 
    while (pagelist.children.length > num) {
        pagelist.removeChild(pagelist.children[num]);
    }
    // 补充所需页面数量的页码列表
    for (let i = pagelist.children.length; i < num; i++) {
        let li = document.createElement('li');
        let a = document.createElement('a');
        a.href = '#' + i;
        a.innerHTML = i + 1;
        li.appendChild(a);
        li.classList.add('page-num');
        // 高亮默认页码
        if (i === 0) {
            li.classList.add('active');
        }
        pagelist.appendChild(li);
    }
    // 找到要点击的目标元素
    let targetElement = pagelist.querySelector('li:nth-child(1) a');
    // 使用click()方法触发点击事件
    targetElement.click();
    //显示当前页码的内容
    showContent();
}
// 按钮状态
function buttonStatus() {
    // 两个按钮都要取消禁用
    prev.forEach(function (item) {
        item.classList.remove('disabled');
    });
    next.forEach(function (item) {
        item.classList.remove('disabled');
    });
    // 如果当前页码是第一页,那么上一页和首页按钮都要禁用
    if (current == 1) {
        prev[0].classList.add('disabled');
        prev[1].classList.add('disabled');
    }
    // 如果当前页码是最后一页,那么下一页和末页按钮都要禁用
    else if (current == num) {
        next[0].classList.add('disabled');
        next[1].classList.add('disabled');
    }
}
//默认显示最开始的内容
showPage();

// 绑定事件

//select改变
showpage.addEventListener('change', function () {
    // 重新计算页数
    pagenum = showpage.value;
    num = Math.ceil(contentNum / parseInt(pagenum));
    // 超出范围的页码要置为最后一页,否则会报错
    if (current > num) {
        current = num;
    }
    // 显示当前页码的内容
    showPage();
});
//点击页码列表
pagelist.addEventListener('click', function (event) {
    // 点击的是页码列表
    if (event.target.tagName === 'A') {
        // 取消当前页码的active
        pagelist.children[current - 1].classList.remove('active');
        // 点击的页码会变成active
        event.target.parentNode.classList.add('active');
        // 更新当前页码
        current = event.target.innerHTML;
        // 显示当前页码的内容
        buttonStatus();
        showContent();
    }
}
);
//绑定按钮事件
//如果当前大于1,那么两个prev都不禁用
//如果当前在最后一页,此时下一页禁用,那么点击后要取消下一页的禁用
for (let i = 0; i < prev.length; i++) {
    prev[i].addEventListener('click', function () {
        if (current > 1) {
            //取消当前页码的active
            pagelist.children[current - 1].classList.remove('active');
            //如果当前页码是最后一页,那么下一页要取消禁用
            next.forEach(function (item) {
                item.classList.remove('disabled');
            });
            if (i == 0)
                //如果是第一个prev,回到第一页
                current = 1;
            else
                //回到上一页
                current--;
            //添加active到当前页码
            pagelist.children[current - 1].classList.add('active');
            //如果当前页码是第一页,那么上一页要取消禁用
            if (current === 1) {
                prev.forEach(function (item) {
                    item.classList.add('disabled');
                });
            }
        }
        showContent();
    }
    )
}
//点击下一页
for (let i = 0; i < next.length; i++) {
    next[i].addEventListener('click', function () {
        if (current < num) {
            //取消当前页码的active
            pagelist.children[current - 1].classList.remove('active');
            //如果当前页码是第一页,那么上一页要取消禁用
            prev.forEach(function (item) {
                item.classList.remove('disabled');
            });
            //回到最后一页
            if (i == 1)
                current = num;
            else
                current++;
            //添加active到当前页码
            pagelist.children[current - 1].classList.add('active');
            //如果当前页码是最后一页,那么下一页要取消禁用
            if (current === num) {
                next.forEach(function (item) {
                    item.classList.add('disabled');
                })
            }
        }
        showContent();
    }
    )
}

//跳转到指定页码
let jump = document.querySelector('.jump-to');
jump.addEventListener('keydown', function (event) {
    if (event.keyCode === 13) {
        // 溢出保护
        if (event.target.value > pagelist.children.length) {
            event.target.value = pagelist.children.length;
        }
        if (event.target.value < 1) {
            event.target.value = 1;
        }
        // 模拟点击事件
        let targetElement = pagelist.querySelector('li:nth-child(' + event.target.value + ') a');
        // 使用click()方法触发点击事件
        targetElement.click();
        buttonStatus();
        showContent();
    }
});


React实现

还是react方便。。。。😟
在这里插入图片描述

1.jsx

import React, { useEffect, useState } from 'react'
import styles from './App.module.css'

export default function App() {
    //创建大小为32的数组,内容为索引+1
    const content = Array.from({ length: 32 }, (_, i) => i + 1);

    const [currentPage, setCurrentPage] = useState([]);//当前页内容
    const [current, setCurrent] = useState(1);//当前页码
    const [pagenum, setPagenum] = useState(5);//每页显示条数
    const num = Math.ceil(content.length / pagenum);//总页数

    const [previousDisabled, setPreviousDisabled] = useState(true);//上一页按钮是否禁用
    const [nextDisabled, setNextDisabled] = useState(false);//下一页按钮是否禁用

    const [inputValue, setInputValue] = useState('');//跳转页码输入框值

    function showContent() {//显示当前页内容
        let start = (current - 1) * pagenum;
        let end = start + pagenum;
        setCurrentPage(content.slice(start, end));
    }
    function buttonStatus() {//按钮状态
        if (current == 1) {//第一页
            setPreviousDisabled(true);
            setNextDisabled(false);
        }
        if (current == num) {//最后一页
            setNextDisabled(true);
            setPreviousDisabled(false);
        }
        if (current != 1 && current != num) {//中间页
            setPreviousDisabled(false);
            setNextDisabled(false);
        }
    }

    useEffect(() => {//页面初始化
        showContent();
        buttonStatus();
    }, [current, pagenum]);

    return (
        <div style={{ padding: '20px' }}>
            <div className={styles.content}>
                {
                    // 创建模拟数据内容
                    currentPage.map((item, index) => (
                        <div key={index}>{item}</div>
                    ))
                }
            </div>
            <div className={styles.page}>
                {/*  向前按钮 */}
                <button className={`${styles.prev} ${previousDisabled ? styles.disabled : ''}`}
                    onClick={() => { setCurrent(1) }}
                >&lt;&lt;</button>
                <button className={`${styles.prev} ${previousDisabled ? styles.disabled : ''}`}
                    onClick={() => { current > 1 ? setCurrent(current - 1) : null }}
                >&lt;</button>
                {/*  页码 */}
                <ul className={styles.pageList}>
                    {
                        Array.from({ length: num }, (_, i) => i + 1).map(item => (
                            <li key={item}
                                className={current === item ? styles.active : ''}
                                onClick={() => {
                                    setCurrent(item);
                                }}>
                                <a href={`#${item}`}>{item}</a>
                            </li>
                        ))
                    }
                </ul>
                {/* 向后按钮 */}
                <button className={`${styles.next} ${nextDisabled ? styles.disabled : ''}`}
                    onClick={() => { current < num ? setCurrent(current + 1) : null }}
                >&gt;</button>
                <button className={`${styles.next} ${nextDisabled ? styles.disabled : ''}`}
                    onClick={() => { setCurrent(num) }}
                >&gt;&gt;</button>
                {/* 页面数量选择框 */}
                <select className={styles.pageSize} onChange={(e) => {
                    setPagenum(parseInt(e.target.value));
                    setCurrent(1);
                    setInputValue(1);
                }}>
                    <option value="5">每页显示5</option>
                    <option value="10">每页显示10</option>
                    <option value="20">每页显示20</option>
                </select>
                {/* 跳转页面输入框 */}
                <span>跳转到</span>
                <input className={styles.jumpTo} type="number" defaultValue={inputValue} value={inputValue}
                    //记录输入框值
                    onChange={(e) => {
                        setInputValue(e.target.value);
                    }}
                    //跳转到指定页码,回车键触发,限制输入范围
                    onKeyDown={(e) => {
                        if (e.key === 'Enter') {//回车键触发
                            let value = parseInt(e.target.value);
                            if (value > num) value = num;//限制输入范围
                            if (value < 1) value = 1;
                            setCurrent(value);//指定页码
                            setInputValue(value);//更新输入框(因为限制输入范围)
                        }
                    }} />
                <span></span>
            </div>
        </div>
    )
}

2. CSS

* {
    font-size: 16px;
    margin: 0;
    padding: 0;
}

a {
    display: inline-block;
    text-decoration: none;
    color: #000;
    padding: 10px 15px;
}

.content {
    display: flex;
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    padding: 20px;
}

.content div {
    width: 220px;
    height: 50px;
    background-color: #78b5e7;
    margin: 10px;
}

.page {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 10px;
    width: 100%;
    height: 100px;
    background-color: #f5f5f5;
}

.page-list {
    list-style: none;
    display: flex;
    flex-direction: row;
}

.prev,
.next,
.page-num {
    border-radius: 4px;
    padding: 0;
    border: 0;
    cursor: pointer;
}

.prev,
.next {
    padding: 10px 15px;
}

.prev:hover,
.next:hover,
.page-num:hover {
    background-color: #e9e7e7;
}

.active {
    background-color: #bee5fc;
}

select {
    font-size: 16px;
    padding: 5px 5px;
    border-radius: 4px;
    border: 1px solid #ccc;
}

.jump-to {
    font-size: 16px;
    padding: 5px 5px;
    border-radius: 4px;
    width: 35px;
    border: 1px solid #ccc;
}

button:active {
    background-color: #fff;
}

.disabled {
    color: #b3b3b3;
    cursor: not-allowed;
    opacity: 0.6;
}
小记

写原生js的时候还是比较慢,后续要加强

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/592161.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python数据分析案例44——基于模态分解和深度学习的电负荷量预测(VMD+BiGRU+注意力)

案例背景 承接之前的案例&#xff0c;说要做模态分解加神经网络的模型的&#xff0c;前面纯神经网络的缝合模型参考数据分析案例41和数据分析案例42。 虽然我自己基于各种循环神经网络做时间序列的预测已经做烂了.....但是还是会有很多刚读研究生或者是别的领域过来的小白来问…

Monorepo(单体仓库)与MultiRepo(多仓库): Monorepo 单体仓库开发策略与实践指南

&#x1f31f; 引言 在软件开发的浩瀚宇宙里&#xff0c;选择合适的代码管理方式是构建高效开发环境的关键一步。今天&#xff0c;我们将深入探讨两大策略——Monorepo&#xff08;单体仓库&#xff09;与MultiRepo&#xff08;多仓库&#xff09;&#xff0c;并通过使用现代化…

Vue3 + Vite + TypeScript + Element-Plus创建管理系统项目

官方文档 Vue3官网 Vite官方中文文档 创建项目 使用npm命令创建项目&#xff1a; npm create vitelatest输入项目名称&#xff1a; ? Project name:项目名选择vue&#xff1a; ? Select a framework: - Use arrow-keys. Return to submit.Vanilla > VueReactPrea…

【网站项目】木里风景文化管理平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

CSS精灵图、字体图标、HTML5新增属性、界面样式和网站 favicon 图标

精灵图 为什么要使用精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度,因此&#xff0c;为了有效地减少服务…

JAVA基础|常用API-JDK8之前传统的日期,时间

一. Date &#xff08;一&#xff09;说明 代表的是日期和时间 &#xff08;二&#xff09;常用的用法 构造器说明public Date()创建一个Date对象&#xff0c;代表的是系统当前此刻日期时间public Date(long time)把时间毫秒值转换成Date日期对象 常见方法说明public long …

算法提高之潜水员

算法提高之潜水员 核心思想&#xff1a;二维01背包 两个容量v1v2注意状态计算时j和p可以<各自的v #include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N 1010,M 80,K 22;int f[K][M];int k,V1,V2;int main(){ci…

FloodFill-----洪水灌溉算法(DFS例题详解)

目录 一.图像渲染&#xff1a; 代码详解&#xff1a; 二.岛屿数量&#xff1a; 代码详解&#xff1a; 三.岛屿的最大面积&#xff1a; 代码详解&#xff1a; 四.被围绕的区域&#xff1a; 代码详解&#xff1a; 五.太平洋大西洋水流问题&#xff1a; 代码详解&#x…

锂电池充放电方式曲线

作为一种“化学能-电能”相互转换的能量装置&#xff0c;锂电池在使用过程中必然会进行充电和放电&#xff0c;合理的充放电方式既能减轻锂电池的损伤程度&#xff0c;又能充分发挥锂电池的性能&#xff0c;具有重要的应用价值。 如《GB/T 31484-2015&#xff1a;电动汽车用动…

非对称齿轮的跨棒距算的对不对

前面有一期咱们聊了非对称齿轮《》&#xff0c;非对称齿轮的齿厚测量一般都为跨棒距。最近研究了下计算方法&#xff0c;对计算结果的正确性做了下验证。 在MATLAB中编制了相关的计算程序&#xff1a; 齿轮的模数4&#xff0c;左侧分度圆压力角25&#xff0c;右侧分度圆压力角…

Sqli-labs第一关到第四关

目录 一&#xff0c;了解PHP源代码 二&#xff0c;破解第一关 2.1在了解完源码之后&#xff0c;我们重点看一下 2.2破解这道题表中有几列 2.3查看表中哪一列有回显 2.4查询库&#xff0c;表&#xff0c;列信息 三&#xff0c;总结 前提&#xff1a; 之所以把1234关…

MySQL基础_1.MySQL概述

文章目录 一、关系型数据库和非关系型数据库1.1 关系型&#xff08;RDBMS&#xff09;1.2 非关系型&#xff08;非RDBMS&#xff09; 二、常用的基础语句2.1 查看表的创建信息2.2 编码问题 一、关系型数据库和非关系型数据库 1.1 关系型&#xff08;RDBMS&#xff09; 是最古…

都上3D数字孪生了,2D的WEB组态和大屏可视化未来的发展在哪里?趋势是基于页面嵌套、蓝图连线等新技术,与功能业务应用融合

首先回顾下组态工具的发展史&#xff1a; 回顾发展史&#xff0c;WEB组态终于可以搭建业务系统了&#xff01;&#xff08;页面嵌套 节点编辑 WEB组态 上位机 大屏可视化 无代码 0代码 iframe nodered 蓝图&#xff09;-CSDN博客文章浏览阅读624次&#xff0c;点赞12次&#x…

ThreeJS:纹理的颜色空间

色彩空间Color Space 在ThreeJS中&#xff0c;纹理的colorSpace属性用于定义文里的颜色空间。 颜色空间是一个用于描述颜色的数学模型&#xff0c;在现实生活中&#xff0c;人眼可以观察到无数种颜色&#xff0c;而颜色空间就是用来描述这些颜色的一个方法&#xff0c;不同的颜…

C语言-自定义类型:结构体,枚举,联合

目录 一、结构体1.1 结构体变量的定义和初始化1.2 结构体内存对齐1.3 修改默认对齐数1.4 结构体传参 二、位段2.1 什么是位段2.2 位段的内存分配2.3 位段的跨平台问题2.4 位段的应用 三、枚举3.1 枚举类型的定义3.2 枚举的优点 四、联合&#xff08;共用体&#xff09;4.1 联合…

c#数据库: 9.删除和添加新字段/数据更新

先把原来数据表的sexy字段删除,然后重新在添加字段sexy,如果添加成功,sexy列的随机内容会更新.原数据表如下: using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Linq; using System.…

Linux理解文件操作 文件描述符fd 理解重定向 dup2 缓冲区 C语言实现自己的shell

文章目录 前言一、文件相关概念与操作1.1 open()1.2 close()1.3 write()1.4 read()1.4 写入的时候先清空文件内容再写入1.5 追加&#xff08;a && a&#xff09; 二、文件描述符2.1 文件描述符 fd 0 1 2 的理解2.2 FILE结构体&#xff1a;的源代码 三、深入理解文件描述…

jupyter notebook 设置密码报错ModuleNotFoundError: No module named ‘notebook.auth‘

jupyter notebook 设置密码报错ModuleNotFoundError: No module named ‘notebook.auth‘ 原因是notebook新版本没有notebook.auth 直接输入以下命令即可设置密码 jupyter notebook password

k8s调度原理以及自定义调度器

kube-scheduler 是 kubernetes 的核心组件之一&#xff0c;主要负责整个集群资源的调度功能&#xff0c;根据特定的调度算法和策略&#xff0c;将 Pod 调度到最优的工作节点上面去&#xff0c;从而更加合理、更加充分的利用集群的资源&#xff0c;这也是我们选择使用 kubernete…

Linux---软硬链接

软链接 我们先学习一下怎样创建软链接文件&#xff0c;指令格式为&#xff1a;ln -s 被链接的文件 生成的链接文件名 我们可以这样记忆&#xff1a;ln是link的简称&#xff0c;s是soft的简称。 我们在下面的图片中就是给test文件生成了一个软链接mytest&#xff1a; 我们来解…
最新文章