分页之美

  实现网站上的分页功能,其实没有什么难度,只要逻辑不出差错就可以正常工作了。至于代码写的怎样也就很少有人关心了,今天把分页按照思路写了一下,然后再进行优化。当然无论写的功能有多少,我们总是希望要能够复用,下次就不用再写了。每次使用也不会出错,健壮性很强。最后呢就是任何一个新人能够在几分钟内读懂代码,维护性好。同时达到这些要求,那就是完美!

  这个分页的功能很一般,分别实现:第一页,最后一页,下一页,上一页,跳到某一页。主要涉及到的参数如下:

  1. rPerPage:每页记录数
  2. rCount:总记录数
  3. dCount:每次显示的可选择的页码(例如当前页为2时,有[1][2][3][4][5][6][7][8][9][10] 十个链接,当前页为14时,有[11]到[20]十个链接)

  在实现的时候,只实现最核心的东西,其它都用回调函数去让外部定制。这样每次使用时自由度也会很大。去掉任何与显示有关的东西,只做加减法。下面是Page类代码:

 1 function Page(rCount/*total records count*/,dispCount/*page count display each time*/,recordPerPage/*records count per page*/,callback,rcallback){
 2     this.rPerPage=recordPerPage;//records count per page
 3     this.pCount= Math.ceil(rCount/this.rPerPage);//total page count
 4     this.dCount=dispCount;//display page number count each time
 5     this.curIdx = 1;//current page index
 6     this.onchange=callback;
 7     this.onrender=rcallback;
 8 }
 9 Page.prototype.next = function(){
10     this.toPage(this.curIdx+1);
11 };
12 Page.prototype.pre = function(){
13     this.toPage(this.curIdx-1);
14 };
15 Page.prototype.first = function(){
16     this.toPage(1);
17 };
18 Page.prototype.last = function(){
19     this.toPage(this.pCount);
20 };
21 Page.prototype.toPage=function(idx){
22     if(idx<1 || idx > this.pCount) return;
23     this.curIdx=idx;
24     if(this.onchange) this.onchange.call(this,idx);
25     this.printp();
26 };
27 Page.prototype.printp=function(){
28     var rg = Math.ceil(this.curIdx/this.dCount);
29     var u=(rg-1)*this.dCount+1,
30         l=Math.min(rg*this.dCount,this.pCount);
31     var d = [];
32     while(u!==l+1) d.push(u++);
33     if(this.onrender) this.onrender.call(this,d);
34 };

  我们看到其实做事情的就两个函数,一个是toPage,一个是printp,其中toPage顾明思义就是跳到指定页,其它的如第一页,上一页等等都调用他即可。他在设置当前页索引的同时调用了onchange函数,并把新的页面索引传给它。这个onchange是我们从外面传入的回调函数至于做什么这边也不用关心了,只要让他知道变化后的页码。另外一个函数printp主要是显示页码的链接的。同样也是在每次索引变化后更新它。并把最新的页码数组传给外部的回调函数。

  这样把Page做为一个类来使用,就可以达到复用的目的,做个简单的测试:

 1 var ctPage = new Page(240,10,10);
 2 ctPage.onchange = function(idx){
 3     console.log("current page:"+idx);
 4 };
 5 ctPage.onrender = function(pn){
 6     console.log("current display numbers:"+pn);    
 7 };
 8 var zxPage = new Page(50,12,11,function(idx){
 9     console.log("zx curPage is:"+idx);
10 },function(pn){
11     console.log("zx numbers:"+pn);
12 });

  这样就初始化了两个分另叫做"ctPage"和"zPage"的对象,他们独立且互不影响。通过运行可以在控制台看到结果:

current page:1
current display numbers:1,2,3,4,5,6,7,8,9,10
current page:18
current display numbers:11,12,13,14,15,16,17,18,19,20
current page:22
current display numbers:21,22,23,24
zx curPage is:2
zx numbers:1,2,3,4,5

  我相信保持这个类的简单性,可以让他更好的工作。至于使用场景也没限制,例如在onchange时,加入一个具有ajax请求的回调函数,这样就可以使用在ajax分页中。虽然简单的测试可以通过,不知道在实际环境中工作怎样,所以边使用边改进,努力让他成为最美的分页。

  这次用了点时间为这个类写了一个小案例,我们先把上面的Page放到js文件pagination.js中,然后在页面中加入引用:

<script src="pagination.js" type="text/javascript"></script>

  在页面中放入需要显示的容器:

<div id="pager"></div>

  我们对这个分页做一个简单的样式:

#pager a,#pager span{
    display: block;
    float: left;
    padding: 0.3em 0.5em;
    margin-right: 5px;
    margin-bottom: 5px;
}
#pager a{
    text-decoration: none;
    border: solid 1px #AAE;
    color: #15B;
}
#pager .current{
    background: #26B;
    color: #fff;
    border: solid 1px #AAE;
}

  下面这段脚本主要把Page类与表现连接起来并绑定交互的事件:

function makeBtn(type,text,clickEvt,scope,attr){
    if(!type || type==="") return;
    var tag = document.createElement(type);
    if(clickEvt)
        tag.onclick = function(e){clickEvt.call(scope,e.target.innerHTML);};for(var key in attr)
        tag.setAttribute(key,attr[key]);
tag.innerHTML = text;
return tag; } function dsp(nums){ var curIdx = this.curIdx, cont = document.getElementById("pager"), p = this,preBtn,nextBtn; cont.innerHTML = ""; cont.appendChild(makeBtn("a","first",p.first,p,{href:"#"})); cont.appendChild(makeBtn("a","prev",p.pre,p,{href:"#"})); nums.forEach(function(item){ if(item===curIdx) cont.appendChild(makeBtn("span",item,null,null,{class:"current"})); else cont.appendChild(makeBtn("a",item,p.toPage,p,{href:"#"})); }); cont.appendChild(makeBtn("a","next",p.next,p,{href:"#"})); cont.appendChild(makeBtn("a","last",p.last,p,{href:"#"})); } function handler(idx){ //do something like request data and update dom console.log("send request idx:"+idx); } var p = new Page(240,10,10,handler,dsp); p.first();

  效果图,貌似是最大众的样式:



  我们也很容易换一种样式:


  上面样式的css:

#pager .current {
    background: #222;
    color: #FFF;
    border-color: #000;
    box-shadow: 0 1px 0 rgba(255,255,255,0.2), 0 0 1px 1px rgba(0, 0, 0, 0.1) inset;
    cursor: default;
}
#pager a:hover {
    text-decoration: none;
    background: #444;
}
#pager a{
      text-decoration:none;
}
#pager a,#pager span {
    float: left;
    color: #CCC;
    font-size:14px;
    line-height:24px;
    font-weight: normal;
    text-align: center;
    border: 1px solid #222;
    min-width: 14px;
    padding: 0 7px;
    margin: 0 5px 0 0;
    border-radius: 3px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.2);
    background: #555; /* Old browsers */
    background: -moz-linear-gradient(top, #555 0%, #333 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#555), color-stop(100%,#333)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top, #555 0%,#333 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top, #555 0%,#333 100%); /* Opera11.10+ */
    background: -ms-linear-gradient(top, #555 0%,#333 100%); /* IE10+ */
    background: linear-gradient(top, #555 0%,#333 100%); /* W3C */
}
#pager{
      padding: 20px 20px 15px 20px;
      background: #444;
      border: 1px solid #333;
      border-radius: 2px;
      height:35px;
      font-family:Arial, Helvetica, sans-serif;
}

 

  上面代码中的handler作为回调函数,可以做一些与服务器的交互和界面的更新(一般不超过10行啦)。而dsp函数主要是维护页码等按钮,代码不到10行。而最上边的makeBtn用来生成按钮的通用函数,不算在此模块里面边,所以我们直接把Page作为模块引用,只负责数据,接着只需要把Page类与“View”连接起来。那么每次就写20行以下的代码,并且保持了最大的灵活性,更重要的是正确性。Less is more.

posted @ 2013-01-30 23:44  月窟仙人  阅读(167)  评论(0编辑  收藏  举报