Ajax非同期通信を使ったページャー表現

2014-07-27

表示する件数が多い場合に、ページ選択アイコンが並びぶページャーをjQuery+非同期通信で実現する方法です。調べるとおそらくはプラグイン等がたくさんあると思うのですが、そこは先ず自分で作ってみてからって事で。

サンプルは郵便局ホームページからダウンロード出来るCSVを利用させて頂きました。

サンプル ダウンロード

HTMLとCSS

/*
 * 表部分
 */
#pager table{
	border: 1px solid #DDDDDD;
	border-collapse: collapse;
  font-size:85%
}
#pager table tr th{
	border: 1px solid #DDDDDD;
	padding:3px;
  background: #828282;
  color:#FFF;
}
#pager table tr td{
	border: 1px solid #DDDDDD;
	padding:5px;
}

/*
 * ページャーアイコン
 */
#ajax_pager{
	height: 30px;
	margin-bottom: 10px;
	line-height: 30px;
	overflow: hidden;
	padding:2px;
}
#ajax_pager a{
	display: block;
	border: 1px solid #DDDDDD;
	float: left;
  padding-left:5px;
  padding-right:5px;
	text-align:center;
	text-decoration:none;
	margin-right:2px;
}
#ajax_pager a:hover{
	background-color: #828282;
	color: #FFFFFF;
}
#ajax_pager .nowpage{
	border: 1px solid #DDDDDD;
	float: left;
  padding-left:5px;
  padding-right:5px;
	height:30px;
	background-color: #828282;
	color: #FFFFFF;
	text-align:center;
	line-height: 30px;
	margin-right:2px;
}
#ajax_pager .page_message{
	float: left;
	height:30px;
  line-height: 30px;
  padding-left: 30px;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript" src="pager.js"></script>

    <div id="pager"><!-- 表を表示する部分 --></div>
    <script type="text/javascript">
       $(function(){
          $('#pager').pager({
              'page_max': '30' //1ページの表示件数
             ,'row'     : ['zip','area','add1','add2'] //tableに表示する項目
             ,'th'      : ['郵便番号','都道府県','住所1','住所2'] //表題
          });
       });
    </script>

オプションで1ページあたりの表示件数と、表示したい項目や表題などが設定出来ます。

pager.js

(function($){
$.fn.pager = function(options){
var settings = $.extend( {
'page_max' : 100
,'row' : ['']
,'th'  : ['']
}, options); 
/*
* データの読み込み
*/
var reading = function(div,nowpage){ 
$("#list_div").html('<img src="loader2.gif" />'); 
var page_max = settings.page_max;
var start = 0;
if( nowpage > 1 ){
start = (page_max*nowpage) - page_max+1; //2ページ目以降は次の開始位置を計算
}
$.ajax({
type: "POST",
url: "pager.php",
data: "page_max="+page_max+"&start="+start,
async:false,
success: function(xml){
var len = $(xml).find("len").text();
page_icon (nowpage,len);
var len = len.toString().replace( /([0-9]+?)(?=(?:[0-9]{3})+$)/g , '$1,' ); //3桁ごとにカンマ
$("#ajax_pager").append('<p class="page_message">データ件数は <strong>'+len+'</strong>件です</p>');
var html = '<table>';
html += '<tr>';
for(var i=0; i<settings.th.length; i++){
html += '<th>'+settings.th[i]+'</th>';
}
html += '</tr>';
$(xml).find("item").each(function(){
html += '<tr>';
for(var i=0; i<settings.row.length; i++){
html += '<td>'+$(this).find(settings.row[i]).text()+'</td>';
}
html += '</tr>';
});
html += '</table>';
$("#list_div").html(html);
}
});
};
/*
* ページアイコンの作成
*/
var page_icon = function(nowpage,len){ 
var p = 1; //ページ開始位置
//7ページ以上ならば、開始ページを1加算
if(nowpage > 6){
p = nowpage-5;
}
$("#ajax_pager").html('<img src="loader2.gif" />'); 
var page = len/settings.page_max; //全件数をページ表示件数で割る
page = Math.ceil(page); //小数点切り上げ
var html = '';
if( nowpage > 1){
var backpage = nowpage-1; //前のページに戻る数
html += '<a href="#"  id="page1">&lt;&lt;</a>';
html += '<a href="#"  id="page'+backpage+'">&lt;</a>';
}
for(var i=0; i<10; i++){ //最大10ページ分のアイコン
if(nowpage == p){
html += '<p class="nowpage">'+p+'</p>'; //現在のページならば
}else{
html += '<a href="#" id="page'+p+'">'+p+'</a>';
}
p++;
if(p >= page) break; //ページ数より大きくなったら停止
}
var nextpage = nowpage+1;
var endpage  = page-1;
if( endpage != nowpage){
html += '<a href="#"  id="page'+nextpage+'">&gt;</a>';
if(page >10) html += '<a href="#" id="page'+endpage+'">&gt;&gt;</a>'; //10ページ以上あるならば
}
$("#ajax_pager").html(html); //ページアイコンの表示
};
/*
* ページをクリックしたら
*/
$(document).on('click','#ajax_pager a',function(e){
var page= $(this).attr("id");
page = page.match(/page(\d+)/)[1]|0; //page123 からpageを除去*数値にする
reading(this,page);
e.preventDefault();
});
/*
* <div id="pager"> 以下に要素の追加
*/
$(this).prepend('<div id="ajax_pager"></div>');
$(this).append('<div id="list_div"></div>');
reading(this,1);
};
})(jQuery)

現在の表示ページや表示件数等をpager.phpに非同期通信で渡す処理や、[1][2][3]といったアイコン表示を行っています。

pager.php

<?php
/*
* サニタイズなどは事前に処理
* ここではとりあえず$_POSTに対して仮の処理
*/
if( is_array($_POST) ){
$data = array();
foreach($_POST AS $key=>$str){
$str = htmlspecialchars($str , ENT_QUOTES , "UTF-8");
$data[$key] = $str;
}
}
/*
* pager.phpに直でアクセスされた場合など、停止させる
* 特にここでデータベースからデータを取り出す場合は注意を!
*/
if(!$data['page_max']) exit; 
$row_all = file('zip.csv'); //ファイルを配列へ
$output = array_slice($row_all, $data['start'], $data['page_max']); // 配列を指定行数から指定分だけ取り出す
header("Content-type: application/xml");
echo '<?xml version="1.0" encoding="UTF-8" ?> ' . "\n";
echo '<rss>'."\n"; 
echo '      <len>'.count($row_all).'</len>'."\n"; //全件数
foreach($output AS $row){
$row = str_ireplace('"',"",$row); //""で括られているので除去
$row = explode(",",$row); //カンマ区切りで配列に
echo '  <item>'."\n"; 
// オプションで指定した 'row' : ['zip','area','add1','add2'] に合わせて名前を付ける
echo '      <zip>'.$row['2'].'</zip>'."\n"; 
echo '      <area>'.$row['6'].'</area>'."\n"; 
echo '      <add1>'.$row['7'].'</add1>'."\n"; 
echo '      <add2>'.$row['8'].'</add2>'."\n"; 
echo '  </item>'."\n"; 
}
echo '</rss>'."\n"; 

非同期通信で送信された$_POSTのサニタイズはご自由に。サンプルは$_POSTをループし、htmlspecialcharsでHTMLエンコードだけの、仮のものを書いています。

ページ数に応じて配列をarray_sliceで指定分だけ取り出し、XML形式で書き出します。ここでデータベースからデータを取り出すのであれば、LIMITで処理しても良いと思います。

注意

pager.phpに直でアクセスされると色々と問題があるので、正しい経路からの処理で有ることを確認する事が大切です。特にデータベースからデータを取り出す場合はより注意が必要だと思います。HTML側をphpにし、Sessionにランダムなワンタイムキーを記録、pager.php側でそれと照合し、OKの場合だけ処理など、対応を忘れずに。