jquery.datatable.js で 入力したページ番号にジャンプする機能を実装する

### デモ

https://codepen.io/qwe001/pen/OYWpQx

画面下部にあるページネーションの「 `…` 」 のリンクをクリックしたら、入力画面が表示されます。
そこに半角数字で値を入れたら、指定したページ番号に一致するレコードを表示します。

### 何がしたいか

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130352/da9b3e00-1ad5-1bd8-af32-858c41197959.png)

jquery.dataTable.jsでページネーションを使用すると、画像みたいなページネーションが自動で生成されます。
ページ遷移も挟まずテーブルのレコード表示を切り替えてくれて大変便利ですね。

ですが、ひとつだけ不満があり。

「 `…` 」の部分、クリックしても何も起きません。
なんだかもったいない気がします。これが膨大なレコード数になった時、
例えば569ページ目の情報にジャンプする場合にチマチマ後ろのほうのページ番号をクリックしまくるのは嫌だなって思いました。

知る人ぞ知る `e*hen*a*.org` というサイトに同様のページジャンプ機能があり、
これが便利だと感じたので、自分でも作ってみようと思いました。

あとついでに、半角数字しか入力できないように制限しています。

### 準備

HTMLを準備します。十分な量のtd要素がないと「 `…` 」は表示されないので、いっぱい `tr>td` 要素を用意してください。100個ぐらい。

```php

<html>
<head>
  <title>test</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
  <link rel="stylesheet" href="https://cdn.datatables.net/plug-ins/f2c75b7247b/integration/bootstrap/3/dataTables.bootstrap.css" />
  <link rel="stylesheet" href="https://cdn.datatables.net/responsive/1.0.4/css/dataTables.responsive.css" />
  <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
  <script type="text/javascript" src="https://cdn.datatables.net/1.10.5/js/jquery.dataTables.min.js"></script>
  <script type="text/javascript" src="https://cdn.datatables.net/plug-ins/f2c75b7247b/integration/bootstrap/3/dataTables.bootstrap.js"></script>
  <script type="text/javascript" src="https://cdn.datatables.net/responsive/1.0.4/js/dataTables.responsive.js"></script>
</head>
<body>
<div class="container">
  <div class="row">
    <div class="col-xs-12">
      <table id="example" class="table table-bordered table-hover">
        <thead>
          <tr>
            <th>aaa</th>
            <th>bbb</th>
            <th>ccc</th>
            <th>ddd</th>
            <th>eee</th>
          </tr>
        </thead>
        <tbody>
          <?php for($i = 0; $i < 100; $i++): ?>
          <tr>
            <td><?php echo $i; ?></td>
            <td><?php echo $i; ?></td>
            <td><?php echo $i; ?></td>
            <td><?php echo $i; ?></td>
            <td><?php echo $i; ?></td>
          </tr>
          <?php endfor; ?>
        </tbody>
      </table>
    </div>
  </div>
</body>
</html>
```

### 実装

jQueryとjquery.datatable.jsを事前に読み込んでいる必要があります。当たり前ですが

```javascript

/**
 * datatable pagination jump
 *
 * @link http://drmsite.blogspot.com/2015/03/datatable-jump-to-page-functionality.html
 * @link https://datatables.net/forums/discussion/32860/how-i-get-id-table
 * @link https://datatables.net/reference/api/draw()
 */
function dataTablePaginationJump(dtElem)
{
  var dtElem = dtElem !== undefined ? dtElem : "table.dataTable";

  var myTable = $(dtElem).DataTable();

  var myTableId = myTable.table().node().id; // e.g. "DataTables_Table_0"
  var myTableEllipsisId = myTableId + "_ellipsis";

  $(document).on('click', "#" + myTableEllipsisId, function(){
    var pageInfo = myTable.page.info();

    var firstPageNum = 1;
    var lastPageNum = pageInfo.pages;

    var num = promptForNumberRange(firstPageNum, lastPageNum);

    if(num){
      num = parseInt(num, 10);
      var index = num - 1;

      myTable.page(index).draw(false);
    }
  });
}

/**
 * Show prompt that are valid only for numeric.
 *
 * @param int minNum
 * @param int maxNum
 * @param str placeholder (optional)
 * @return int or bool(false)
 */
function promptForNumberRange(minNum, maxNum, placeholder)
{
  var minNum = parseInt(minNum, 10);
  var maxNum = parseInt(maxNum, 10);

  if(placeholder === undefined){
    var placeholder = "確認したいページ番号を半角数字で入力してください。\n有効な値は次の範囲です。 " + minNum + " ～ " + maxNum;
  }

  var inputVal = window.prompt(placeholder, "");

  if(inputVal === null){ // cancel button pressed.
    return false;
  }

  var inputNum = parseInt(inputVal, 10);

  return checkNumberRange(minNum, maxNum, inputNum);
}

/**
 * Check if input value is within the specified numerical range.
 *
 * @param int minNum
 * @param int maxNum
 * @param int inputNum
 * @return int
 * @link https://stackoverflow.com/questions/15047140/javascript-prompt-number-and-continue-prompting-if-answer-is-wrong
 */
function checkNumberRange(minNum, maxNum, inputNum)
{
  var minNum = parseInt(minNum, 10);
  var maxNum = parseInt(maxNum, 10);
  var inputNum = parseInt(inputNum, 10);

  if(inputNum >= minNum && inputNum <= maxNum){
    return inputNum;
  }
  else if(isNaN(inputNum)){
    var errorText = "入力された値が半角数値ではありません。\n有効な値は次の範囲です。 " + minNum + " ～ " + maxNum;
    return promptForNumberRange(minNum, maxNum, errorText);
  }
  else {
    var errorText = "入力された値(" + inputNum + ") は有効な値ではありません。\n有効な値は次の範囲です。 " + minNum + " ～ " + maxNum;
    return promptForNumberRange(minNum, maxNum, errorText);
  }
}
```

### 実行

```javascript

$(function(){
  $('table#example').DataTable();
  dataTablePaginationJump('table#example');
});
```

デモではセレクタをid指定していますが、クラス指定でも動作します。
その場合、tableには「DataTables_Table_0」みたいなid属性が自動で付与されます。(jquery.datatable.jsによって自動で付与される)

あと、tableタグに「dataTable」というクラスを付与していれば、関数に引数を入力不要にしています。

```javascript

$(function(){
  $('table.dataTable').DataTable();
  dataTablePaginationJump();
});
```

### 余談

どうでもいいですけど、サンプル用のtdタグはEmmetを使って次のコードから生成しました

```
tr*1000>th{$}*5
```

デフォルトは1ページ毎に10行表示なので、100ページ分ですね。