【JavaScript】JS实用案例分享:动态生成分页组件 通过按键实现移动控制_动态分页按钮

border-radius: 0 4px 4px 0;
}

.pagination li:first-child {
box-shadow: none !important;
}

.pagination li.current {
border-color: #1890ff;
color: #1890ff;
border-left: 1px solid #1890ff;
}

.pagination li.current:not(:first-child) {
margin-left: -1px;
}


### 案例需求


界面中存在`id=jsContainer`的`节点A`,系统会**随机**实例化各种`Pagination`**实例**,按照如下要求补充完成`Pagination`函数。


1. 最多连续显示5页,居中高亮显示`current`页(如`demo1`所示):

 ![在这里插入图片描述](https://img-blog.csdnimg.cn/059092d5b9694c42bb45ceba7cb341bb.png)
2. `total <= 1` 时,隐藏该组件(如`demo2`所示):

 ![在这里插入图片描述](https://img-blog.csdnimg.cn/bad87fe4265b431ba695f9f0fcfbfe4d.png)
3. 如果`total<=5`,则显示全部页数,隐藏“首页”和“末页”元素(如`demo3`所示):

 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3d40729634594436b14ec72526e05714.png)
4. 当`current`**居中不足5页**,向后(前)补足5页,隐藏“首页”(“末页”)元素(如`demo4`和`demo5`所示):

 ![在这里插入图片描述](https://img-blog.csdnimg.cn/fbc3ec01b6984a279c4e10c4c0910f55.png)
5. `total`、`current`均为正整数,`1 <= current <= total`
6. 上面效果演示是 `new Pagination(document.getElementById('jsContainer'), 16, 9)` 执行后的结果


### JavaScript实现



function Pagination(container, total, current) {
this.total = total;
this.current = current;
this.html = html;
this.el = document.createElement(‘ul’); //TODO: 创建分页组件根节点
this.el.className = ‘pagination’
if (!this.el) return;

this.el.innerHTML = this.html();
container.appendChild(this.el);

// 分页组件根节点内容为空时,添加hide类名隐藏
if (!this.el.innerHTML) {
    this.el.className = 'pagination hide'; //TODO: 判断是否需要隐藏当前元素

}

function html() {
    if (this.total <= 1) return '';
    let str = ''
    //TODO: 生成组件的内部html字符串
    if (this.total <= 5) {
        for (let i = 1; i <= this.total; i++) {
            str += i == this.current ? `<li class="current">${this.current}</li>` : `<li>${i}</li>`
        }
    } else {
        if (this.current <= 3) {
            for (let i = 1; i <= 5; i++) {
                str += i == this.current ? `<li class="current">${this.current}</li>` : `<li>${i}</li>`
            }
            str += '<li>末页</li>'
        }
        if (this.current > 3 && this.current < this.total - 2) {
            str += '<li>首页</li>'
            for (let i = this.current - 2; i <= this.current + 2; i++) {
                str += i == this.current ? `<li class="current">${this.current}</li>` : `<li>${i}</li>`
            }
            str += '<li>末页</li>'
        }
        if (this.current >= this.total - 2) {
            str += '<li>首页</li>'
            for (let i = this.total - 4; i <= this.total; i++) {
                str += i == this.current ? `<li class="current">${this.current}</li>` : `<li>${i}</li>`
            }
        }
    }
    return str;
}

}
// 测试
new Pagination(document.getElementById(‘jsContainer’), 16, 20)


这个案例中`Pagination`是一个构造函数,需要通过`new`操作符调用,需要注意的是`Pagination`内的方法(如`html`方法)需要先挂载到`Pagination`上(`this.html = html`)才能被`Pagination`中的其它成员访问(通过`this`访问`this.html()`)


**知识点:**


* [new 运算符](https://bbs.csdn.net/topics/618545628)与构造函数的使用。


## 2、通过按键实现移动控制


### 效果演示


![在这里插入图片描述](https://img-blog.csdnimg.cn/67408caf0498428c920f976c73a1bd2b.gif#pic_center)


有以下`HTML`和`CSS`:


HTML结构




CSS样式



table.game {
font-size: 14px;
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
table.game td {
border: 1px solid #e1e1e1;
padding: 0;
height: 30px;
text-align: center;
}
table.game td.current{
background: #1890ff;
}


### 案例需求


界面中存在`id=jsContainer`的`节点A`,系统会随机生成`class`为`game`的 `m`行 `n`列表格(`m >= 1, n >= 1`),并**随机选中**一个`td`节点,请按照如下需求实现`bind`函数


1. `bind` 函数为`document`绑定`keydown`事件,当系统触发`上(键值38)`、`下(键值40)`、`左(键值37)`、`右(键值39)`按键时,请找到当前选中的`td`节点,并根据当前指令**切换高亮节点**,具体效果参考上面的效果演示。
2. **在第一列往左移动则到达最后一列**;**在最后一列往右移动则到达第一列**;**在第一行往上移动则到达最后一行**;**在最后一行往下移动则到达第一行**;
3. 当前界面为系统在`节点A`中生成 `9 * 9` 表格并随机选中一个`td`节点后的效果。


### JavaScript实现



function bind() {

document.onkeydown = event => {
if (!event) return;
var code = event.keyCode || ‘’;
if (!{ ‘37’: 1, ‘38’: 1, ‘39’: 1, ‘40’: 1 }[code]) return;
event.preventDefault && event.preventDefault();
//TODO: 请实现按键控制

   // 当前元素
   const nowCode = document.getElementsByClassName('current')[0]
   // 当前元素的父节点的所有子字节的集合
   const brotherItems = nowCode.parentElement.getElementsByTagName('td')
   // 获取当前元素在父元素中的下标
   // 通过数组的indexOf方法查找nowCode在brotherItems中的下标
   // 因为brotherItems是伪数组,不算真正意义上的数组,不能直接使用indexOf方法,
   // 所以这里通过在随便一个数组上使用indexOf([].indexOf),然后通过call改变indexOf方法的this指向,使其指向到brotherItems
   // 这样做之后就相当于是在brotherItems身上执行indexOf方法,call方法的第一个参数之后的参数会作为indexOf方法的参数
   const index = [].indexOf.call(brotherItems, nowCode)

   // 去除类名
   nowCode.className = ''

   switch (code) {
       // 向左移动
       case 37:
           // previousElementSibling获取上一个兄弟节点
           if (nowCode.previousElementSibling) {
               // 如果上个兄弟节点存在,则为上个兄弟节点添加class
               nowCode.previousElementSibling.className = 'current'
           } else {
               // 如果上个兄弟节点不存在,则为父节点的最后一个孩子节点添加class
               // parentElement获取父节点
               // lastElementChild获取最后一个孩子节点
               nowCode.parentElement.lastElementChild.className = 'current'
           }
           return;
       // 向右移动
       case 39:
           // nextElementSibling获取下一个兄弟节点
           if (nowCode.nextElementSibling) {
               // 如果下个兄弟节点存在,则为下个兄弟节点添加class
               nowCode.nextElementSibling.className = 'current'
           } else {
               // 如果下个兄弟节点不存在,则为父节点的第一个孩子节点添加class
               // parentElement获取父节点
               // firstElementChild获取第一个孩子节点
               nowCode.parentElement.firstElementChild.className = 'current'
           }
           return;
       // 向上移动
       case 38:
           if (nowCode.parentElement.previousElementSibling) {
               // 如果父元素的上个兄弟节点存在,则为父元素上个兄弟节点的所有孩子节点中下标为index的元素添加class
               nowCode.parentElement.previousElementSibling.getElementsByTagName('td')[index].className = 'current'
           } else {
               // 如果父元素的上个兄弟节点不存在,则为父元素的父元素(爷元素)的最后一个节点的所有孩子节点中下标为index的元素添加class
               nowCode.parentElement.parentElement.lastElementChild.getElementsByTagName('td')[index].className = 'current'
           }
           return;
       // 向下移动
       case 40:
           if (nowCode.parentElement.nextElementSibling) {
               // 如果父元素的下个兄弟节点存在,则为父元素下个兄弟节点的所有孩子节点中下标为index的元素添加class
               nowCode.parentElement.nextElementSibling.getElementsByTagName('td')[index].className = 'current'
           } else {
               // 如果父元素的下个兄弟节点不存在,则为父元素的父元素(爷元素)的第一个节点的所有孩子节点中下标为index的元素添加class
               nowCode.parentElement.parentElement.firstElementChild.getElementsByTagName('td')[index].className = 'current'
           }
           return;

   }

};
}
// 调用测试
bind()


这个案例主要考察了对`JavaScript` `DOMApi`的运用,需要注意的是通过`DOMApi`获取的元素集合是[HTMLCollection](https://bbs.csdn.net/topics/618545628)类型的伪数组对象,它并不能算是真正意义上的数组,数组上的一些方法对它也不适用,这与[arguments](https://bbs.csdn.net/topics/618545628)相似。


**知识点:**


* [previousElementSibling](https://bbs.csdn.net/topics/618545628) 返回当前元素在其父元素的子元素节点中的**前一个**元素节点,如果该元素已经是第一个元素节点,则返回 null, 该属性是只读的。
* [nextElementSibling](https://bbs.csdn.net/topics/618545628) 返回当前元素在其父元素的子元素节点中的**后一个**元素节点,如果该元素已经是最后一个元素节点,则返回 null, 该属性是只读的。
* [parentElement](https://bbs.csdn.net/topics/618545628) 返回当前节点的父元素节点,如果该元素没有父节点,或者父节点不是一个 DOM 元素,则返回 null。
* [lastElementChild](https://bbs.csdn.net/topics/618545628) 返回对象的**最后一个**子元素,如果没有子元素,则返回 null。
* [firstElementChild](https://bbs.csdn.net/topics/618545628) 只读属性,返回对象的**第一个**子元素, 如果没有子元素,则为 null。
* [call()](https://bbs.csdn.net/topics/618545628) 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。


## 结语



> 
> 这篇文章的所有内容都出自于[牛客网的JS篇题库](https://bbs.csdn.net/topics/618545628):  
>  ![在这里插入图片描述](https://img-blog.csdnimg.cn/4bda5af2d92143c98cbb56d82442173f.png)
> 
> 
> 


牛客网的`JS`题库非常贴合实际的,在写的过程中自己查漏补缺,收获了很多,强烈将[牛客网](https://bbs.csdn.net/topics/618545628)推荐给大家!


如果本篇文章对你有所帮助,还请客官一件四连!❤️



> 
>  [**基础不牢,地动山摇!** 快来和博主一起来牛客网刷题**巩固基础知识**吧!](https://bbs.csdn.net/topics/618545628)
> 
> 
> 




![img](https://img-blog.csdnimg.cn/img_convert/3f80a79f3510e3c598315f316ce93873.png)
![img](https://img-blog.csdnimg.cn/img_convert/843f503f0201a378fb4a7841be5123bd.png)
![img](https://img-blog.csdnimg.cn/img_convert/2f3a77485ca9ca1bee71a4e89badbe67.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

1714723294586)]
[外链图片转存中...(img-mXgAJgeV-1714723294587)]
[外链图片转存中...(img-XJqEzTRP-1714723294588)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值