Javascript分页组件:xPagination.js

加入新公司后,一直忙得飞起。由于在新公司的工作包括写一些公用组件,比如日历啊,分页啊什么的,这个分页组件我觉得要用的人比较多,所以把它独立了出来,做成了一个无依赖的原生JS版本,供需要的人使用。

注:新公司使用了一个自己的框架,写组件的方式与jQuery或原生JS都不一样,所以要做提取工作。

xPagination.js参数说明,示例,在线预览请见:http://wslx520.github.io/pagination/index.html

xPagination.js源码请见:Github

本文只是说明一下写这个组件的一些思路,仅供参考。

更新记录:

2015/12/31: 修复一个大意之下的bug:点下一页按钮无效(会跳到第1页)

原由:以前用pages局部变量保存了总页数(如果不传则是0),但会在后面修复一次,但我把生成页码的函数独立出去后,相当然的删除了重设pages变量的代码,结果导致pages一直是0.

解决办法:直接调用options.pages,这个的值是会正确设置的。

—————–

这个组件起源比较仓促,我本来是去总部“学习”这个框架的组件开发方式的,当时写了一个简单的,后来才要我完善,再后来我就提取出来了一个纯JS版本,再后来我就要同时维护两个版本了,今天同时更新了两个版本,功能基本确定下来了,所以我决定发布此文。

分页组件大家见了很多,样式本文就不提了,我会说一些JS上的实现逻辑。

最初的版本,我打算让分页组件可以自定义最多显示多少页是否显示上下页按钮,首页末页按钮;且在跳页码的时候要触发一个函数(供发起ajax请求之类的用)。

然后新增了需求:要显示省略页,比如最多同时显示8页,你却有100页,那么,8页后面的就要显示个省略号,让用户知道后头还有。

这个需求就引出了另一个需求:当我翻到靠后的页码时,那么前面也要显示省略号;当当前页十分靠近第一页或最后一页时,则要隐藏前面或后面的省略号。

当然,还有个必须要有的需求:当在第1页时,则上一页与首页按钮要禁用;在最后一页时,则下一页与末页按钮要禁用

当时还出了个我印象比较深的问题,就是总数只有一页时,则上下页按钮,首末页按钮,全都要禁用。而当时我的判断逻辑用的是else if,也就是想当然的认为当前页等于1,就不会等于总页数——结果总页数就是1,结果出现只禁用了上一页按钮,但下一页按钮还可以点。

html结构:

那么html结构就会是这样:

首页-上一页-省略号-1,2,3,4-省略号-下一页-末页

这时候我又想到,如果当前最多显示8页,但实际上总页数只有5页,则用不着生成省略号了。那么就又多了个判断:如果有总页数超出最大同时显示页数,才生成省略号。

然后就是具体代码实现。而我在这里走了弯路。

首先会了节省代码提高效率,我用一个数组来lis保存所有页码的html字符串,最后join并插入到对应节点

比如共100页,最多同时显示8页,那我翻到第9页时,就要把所有页码刷新一遍,从原来的1-8变成2-9,且前面的省略号就要显示出来了;当页码靠近最后一页时,则后面的省略号就要隐藏了。

为了控制两个省略号的显示与隐藏,我打算把他们缓存成变量,到时候直接hide(xxx)就行了。

但缓存这些元素的难点在于,我的上下页首末页按钮是可要可不要的,而我的页码全是用innerHTML生成的,所以没法直接在生成的时候得到对应的元素,而必须通过计算是否有上一页,首页,算出对应情况下的省略号的index,然后缓存下来。

在这里我出了好几个bug,都是比如没有首末页按钮时正常,有人加入了这两个按钮,而我的index没取对,就报错了。

最后我写了很长一串的三元运算来计算正确的省略号index。

而对于上下页按钮,首末页按钮的index,也需要这样搞。

所幸的是我们出了个分页组件的规范文档,里面没有提到过要首末页功能。

但是,页码生成的规范却改了,

新的规范是这样:

上一页-1-省略号,12,13,14-省略号-44-下一页

也就是:始终显示1与最后一页的页码,省略号出现在中间与首末页之间。所以,很显然不再需要显示“首末页”按钮了。

但之前的页码生成规则就要推翻了。

相同的是,我还是打算只在总页数超出最多显示页数时才生成省略号。

改成后面的规范之后,其实总的dom元素更少了。我在忙了半个月其他事后,重新理了一下生成页码的思路。

首先,页码最少有一页

所以我直接给lis数组push了一页。

然后,循环push 2,3,4。。。这样的页码,注意,这里的循环是从2开始了——当最大页就是1页时,此循环压根不会进入。

再然后,就是输出最后一页的页码——最后的页码数字始终不会变化。这里也需要判断一下:只有当最后一页大于1时,才有必要输入最后一页。

页码全部push进去以后,再判断是否有上一页的参数:prev,有就用unshift加入上一页;下一页则用push

最后,依然是join数组并附给对应的innerHTML。

然后就是之前就已经遇到过的难题了:怎么判断出上一页下一页及两个省略页的index?

一位同事提醒我,说我不应该写这么复杂的判断,直接循环所有页码,针对页码的情况做相应操作。之前我认为从性能上考虑,这样是不可取的,后来觉得这样对性能造不成多严重的拖累——因为,我改变中间部分的页码时,也是通过循环,就算加入上下页与省略页,也就是多了4个而已。

而通过前面产生innerHTML的过程我知道,页码中的上下页,省略页,是生成之后永远不会改变的,最多就是禁用,启用,隐藏与显示,其他数字页码,会改变里面的数字——比如从2-7变成3-8。

于是,我把每个页码都加了个ui-page属性——这本来是用来保存此页码的真实页数的,但我把上下页分别设为了prev与next,省略页则是ellipsis,当我循环页码队列时,如果是数字,就改变innerHTML(这里我先判断当前页是否真需要改变,否则不变);如果是这三个单词,则判断当前页与总页数的关系,进行隐藏与显示等操作。

这里用到了一个技巧:当两个函数A与B,需要的参数是一样的,而我要根据不同的条件,分别调用两个函数,该怎么做?

答案是:

1
(aaa ? A : B)(arg1, arg2);

比如在循环页码时,当 当前页是1时,我就要禁用prev,否则我就要启用prev,而禁用启用是通过addClass与removeClass来做的,二者需要的参数是一样的。

页码生成算法

分别有两种页码生成算法,相同的是,当前页要始终处于所以页码的中央位置(当然,太靠前或靠后就例外了)。一种是中间部分的数字页码全需要更新的时候,我写了个函数,用来计算出现在的页码该从哪个数开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 根据当前页,总数页,最多显示页计算出应显示的页码
pageCalc = function (curr, pages, max) {
//console.log('curr,pages,max',curr,pages,max)
var start = 1,
// var end = max;
//var truehalf = max / 2;
half = Math.floor(max / 2),
maxLength = Math.min(pages, max) - 1;
// if (pages < end) { // end = pages; // } //console.log('start,half,max',start,half,max) //如果总页数已超过最多显示页,且当前页大于最多显示页的一半,当前页放中间 if (pages > max && curr >= half) {
start = curr - half;
// end = (curr + half) > pages ? pages : curr + half;
if (curr === pages - 1) {
// start = curr - half -(half pages) {
start = pages - maxLength;
}
start = start > 0 ? start : 1;
return start;
}

这个函数需要3个参数,依次是当前页,总页数,和最多同时显示的页数,执行后返回一个start,即该从哪一页开始。其中有很重要的一点,就是如果start+最大显示页,已经超过了总页数,那就应该把start往前移了,让start生成的页码正好在最后一页结束(同时,这个时候也应该隐藏末尾端的省略号了)。

我的分页组件中,有个max参数指定最多同时显示页,比如8页,7页,所以他存在奇偶数的问题,在计算页码的时候要特别注意。

改需求后的算法

改需求后,由于第1页与最后一页,始终数字都是不变的,所以刷新页码的时候直接就可以无视他们。这个算法就比较简单了,只涉及到逻辑问题:

1
2
3
4
5
6
7
// 默认假设页码居中
var start = page - (Math.ceil(half - 1) - 1);
if (page <= Math.ceil(half)) { // 页码靠前 start = 2; } else if (page >= Math.ceil(pages - half)) {
// 页码靠后
start = Math.floor(pages - half);
}
setPageNumbers(start);

里面的half是max/2(未四舍五入的),page是传入的要跳至的页码(也就是会变成curr)。我们以两个不同的实际场景来说明这几句算法:

1,max=8,pages=21

此时half是整数4

1)当curr(当前页,我用[]包起来表示)为默认1时,html结构是这样的:

[1],2,3,4,5,6,7,…,21

2)此时,当curr>4时,比如5,html结构会变成:

1,…,3,4,[5],6,7,8,…,21

3)当curr靠后时,比如17:

1,…,15,16,[17],18,19,20,21

4)当curr<17时,比如16:

1,…,14,15,[16],17,18,19,…,21

此时实际上16的位置与2)下的5位置相同

2,max=7,pages=21

此时half是3.5

1)默认curr=1时

[1],2,3,4,5,6,…,21

与上一种情况,少了一页

2)当curr=5

1,…,3,4,[5],6,7,…,21

3)当curr>17,比如18

1,…,16,17,[18],19,20,21

4)当curr=17

1,…,15,16,[17],18,19,…,21

妈呀累死我了,简单地说就是通过以上推断,得出了最终的计算公式

该省事儿的时候要省事儿

比如,pages<=max时,省略号就没有了,此时应该直接改变页码,将对应index的页码设为active,就行了,再也不用过循环。

重新算页码的情况

比如,我初始化时,size=15,items=300,则总页码是20页;用着用着我点了一下”30″,也就是每页变成30条了,则总页码应该变成10了。

更糟糕的是,如果你此时正在第20页,总页码变成10了,你该去哪儿?

所以我就必须重置页码,并选中与当前页码最接近的页码。

这只是一种思路,所以我才会把生成页码的函数独立出来。

发表评论

电子邮件地址不会被公开。