tablesorterpagerでページ指定アクセスを可能にする

#tablesorterpagerとは？

テーブルにソート機能を付けるjqueryライブラリ[tablesorter](http://tablesorter.com/docs/)にページ指定機能を付与するアドオンです。
入手は上記リンクのDownloadの項から。

#デフォルトのtablesorterpager

で、このtablesorterpagerデフォルトの導入サンプルが公式に掲載されています。
[こちら](http://tablesorter.com/docs/example-pager.html)がサンプルページ。

![pager.png](https://qiita-image-store.s3.amazonaws.com/0/110893/17ffe43f-fffc-3415-914c-3cc2f8d7f089.png)
拡大するとこんな感じ。
機能としては

- 最初のページへ
- 前のページへ
- 次のページへ
- 最後のページへ
- １ページあたりの表示件数変更
- 現在のページ番号と総ページ数表示

この６つです。
そう、この中に私の求めていた**ページを指定してジャンプする機能**が無かったのです！

#ページ指定アクセス機能の実装
##ソース解析

他のライブラリを探しても良かったんですがtablesorterpagerのソースを見てみるとこの足りない機能の実装ができるのでは？
と思ったので実装してみる事にしました。
以下のような最近良く見るbootstrapページャに変更します。

![修正後pager.png](https://qiita-image-store.s3.amazonaws.com/0/110893/c323113c-b4ad-1c16-1189-a5b4b44ee4a7.png)


ソースは以下のような構造をしています。

```java:jquery.tablesorter.pager.js
：
中略
：
  function moveToPrevPage(table) {
  var c = table.config;
  c.page--;
  if(c.page <= 0) {
    c.page = 0;
  }
  moveToPage(table);
}
：
中略
：
this.defaults = {
  size: 10,
  offset: 0,
  page: 0,
  totalRows: 0,
  totalPages: 0,
  container: null, 
  cssNext: '.next',
  cssPrev: '.prev',
  cssFirst: '.first',
  cssLast: '.last',
  cssPageDisplay: '.pagedisplay',
  cssPageSize: '.pagesize',
  seperator: "/",
  positionFixed: true,
  appender: this.appender
};
this.construct = function(settings) {
  return this.each(function() {	
    config = $.extend(this.config, $.tablesorterPager.defaults, settings);
    var table = this, pager = config.container;
    $(this).trigger("appendCache");
    config.size = parseInt($(".pagesize",pager).val());
    $(config.cssFirst,pager).click(function() {
      moveToFirstPage(table);
      return false;
    });
    $(config.cssNext,pager).click(function() {
      moveToNextPage(table);
      return false;
    });
    $(config.cssPrev,pager).click(function() {
      moveToPrevPage(table);
      return false;
    });
    $(config.cssLast,pager).click(function() {
      moveToLastPage(table);
      return false;
    });
    $(config.cssPageSize,pager).change(function() {
      setPageSize(table,parseInt($(this).val()));
      return false;
    });
  });
};
```

```HTML:pager.html
<div id="pager" class="pager">
  <form>
    <img src="../addons/pager/icons/first.png" class="first"/>
    <img src="../addons/pager/icons/prev.png" class="prev"/>
    <input type="text" class="pagedisplay"/>
    <img src="../addons/pager/icons/next.png" class="next"/>
    <img src="../addons/pager/icons/last.png" class="last"/>
    <select class="pagesize">
      <option selected="selected"  value="10">10</option>
      <option value="20">20</option>
      <option value="30">30</option>
      <option  value="40">40</option>
    </select>
  </form>
</div>
```

【最初のページへ】リンクをクリックした時は【最初のページへ】関数を実行するような構造をしていたので
ここに【ページ指定】リンクをクリックした時【ページ指定】関数を実行するようにすれば良さそうです。

##改修点１

**関数の追加**

ページ番号をクリックした時の関数を追加します。
ページ番号の指定は第２引数で持ってきています。

```java:jquery.tablesorter.pager.js
：
中略
：
  function moveToPrevPage(table) {
  var c = table.config;
  c.page--;
  if(c.page <= 0) {
    c.page = 0;
  }
  moveToPage(table);
}
// 追加部分開始
function moveToDirectPage(table, nowpage) {
  var c = table.config;
  c.page = nowpage;
  moveToPage(table);
}
// 追加部分終了
：
中略
：
```

##改修点２

**イベントハンドラ設定**

ページ番号リンクをクリックした時のクラスの設定をします。

```java:jquery.tablesorter.pager.js
：
中略
：
this.defaults = {
  size: 10,
  offset: 0,
  page: 0,
  totalRows: 0,
  totalPages: 0,
  container: null, 
  cssNext: '.next',
  cssPrev: '.prev',
  cssFirst: '.first',
  cssLast: '.last',
// 追加部分開始
  cssDirect: '.direct',
// 追加部分終了
  cssPageDisplay: '.pagedisplay',
  cssPageSize: '.pagesize',
  seperator: "/",
  positionFixed: true,
  appender: this.appender
};
：
中略
：
```

##改修点３

**イベントハンドラの中身修正**

ここが一番肝で長いです。

- 【最初のページへ】リンクをクリックした時
 1. 【最初のページへ】リンクdisabled
 2. 【前のページへ】リンクdisabled
 3. 【次のページへ】リンクdisabled解除
 4. 【最後のページへ】リンクdisabled解除
 5. 【１ページ目】リンク選択中
- 【前のページへ】リンクをクリックした時
 1. １ページ目だった時【最初のページへ】リンクdisabled
 2. １ページ目だった時【前のページへ】リンクdisabled
 3. 【次のページへ】リンクdisabled解除
 4. 【最後のページへ】リンクdisabled解除
 5. 【現在ページ】リンク選択中
- 【次のページへ】リンクをクリックした時
 1. 最終ページだった時【最初のページへ】リンクdisabled
 2. 最終ページだった時【前のページへ】リンクdisabled
 3. 【前のページへ】リンクdisabled解除
 4. 【最初のページへ】リンクdisabled解除
 5. 【現在ページ】リンク選択中
- 【最後のページへ】リンクをクリックした時
 1. 【最後のページへ】リンクdisabled
 2. 【次のページへ】リンクdisabled
 3. 【前のページへ】リンクdisabled解除
 4. 【最初のページへ】リンクdisabled解除
 5. 【最終ページ】リンク選択中
- ページ指定リンクをクリックした時
 1. すべてのdisabledを解除
 2. １ページ目だった時【最初のページへ】リンクdisabled
 3. １ページ目だった時【前のページへ】リンクdisabled
 4. 最終ページだった時【最初のページへ】リンクdisabled
 5. 最終ページだった時【前のページへ】リンクdisabled
 6. 【現在ページ】リンク選択中

```java:jquery.tablesorter.pager.js
$(config.cssFirst,pager).click(function() {
  moveToFirstPage(table);
// 追加部分開始
  $('.first').addClass('disabled');
  $('.prev').addClass('disabled');
  $('.next').removeClass('disabled');
  $('.last').removeClass('disabled');
  $('.pagination li').removeClass('active');
  $('.p_0').addClass('active');
// 追加部分終了
  return false;
});
$(config.cssPrev,pager).click(function() {
  moveToPrevPage(table);
// 追加部分開始
  if (config.page <= 0) {
    $('.first').addClass('disabled');
    $('.prev').addClass('disabled');
  }
  $('.next').removeClass('disabled');
  $('.last').removeClass('disabled');
  $('.pagination li').removeClass('active');
  $('.p_'+config.page).addClass('active');
// 追加部分終了
  return false;
});
$(config.cssNext,pager).click(function() {
  moveToNextPage(table);
// 追加部分開始
  if (config.page >= (config.totalPages - 1)) {
    $('.next').addClass('disabled');
    $('.last').addClass('disabled');
  }
  $('.first').removeClass('disabled');
  $('.prev').removeClass('disabled');
  $('.pagination li').removeClass('active');
  $('.p_'+config.page).addClass('active');
// 追加部分終了
  return false;
});
$(config.cssLast,pager).click(function() {
  moveToLastPage(table);
// 追加部分開始
  var nowpage = config.totalPages - 1;
  $('.first').removeClass('disabled');
  $('.prev').removeClass('disabled');
  $('.next').addClass('disabled');
  $('.last').addClass('disabled');
  $('.pagination li').removeClass('active');
  $('.p_'+nowpage).addClass('active');
// 追加部分終了
  return false;
});
// 追加部分開始
$(config.cssDirect,pager).click(function() {
  moveToDirectPage(table,   $(this).attr('page'));
  $('.pagination li').removeClass('disabled');
  $('.pagination li').removeClass('active');
  $(this).addClass('active');
  if (  $(this).attr('page') <= 0) {
    $('.first').addClass('disabled');
    $('.prev').addClass('disabled');
  }
  if (  $(this).attr('page') >= table.config.totalPages - 1) {
    $('.next').addClass('disabled');
    $('.last').addClass('disabled');
  }
  return false;
});
// 追加部分終了
```

##改修点４

**HTML**

- テキストボックス(pagedisplay)はhiddenパラメータに
- プルダウン(pagesize)もhiddenパラメータに
- ページ表示はDBなどから取得した件数を元にページ数を出して直接(forやforeach)記述します。
- 内部的にはページ番号は0から始まるのでタグ内は0から記述します。

```html
<nav>
  <form id="pager">
    <ul class="pagination">
      <li class="first disabled"><a href="#">««</a></li>
      <li class="prev disabled"><a rel="prev" href="#">«</a></li>
      <li class="direct p_0 active" page="0"><a href="#">1</a></li>
      <li class="direct p_1" page="1"><a href="#">2</a></li>
      <li class="direct p_2" page="2"><a href="#">3</a></li>
      <li class="direct p_3" page="3"><a href="#">4</a></li>
      <li class="direct p_4" page="4"><a href="#">5</a></li>
      <li class="next"><a rel="next" href="#">»</a></li>
      <li class="last"><a href="#">»»</a></li>
    </ul>
    <input type="hidden" class="pagedisplay"/>
    <input type="hidden" class="pagesize" value="10">
  </form>
</nav>
```

#最後に

これでページ指定アクセスが可能になったはずです。
注意点として**表示上は１ページからですが内部的には0から始まっています**。
この点にだけ気をつけていただければと思います。

１ページ内にすべてのレコードをhtmlで書いてそれをページング処理しています。
流石に10000件とかある場合重くなるので別の方法を考えたほうが良いと思います。