jiichan.com

PROGRAMMING

ページャーを作る

サイトを構築していると必ずといっていいほどページャーが必要になってきます。
ブログなど日常的にコンテンツが増えるていくページは、ページ分けしなければ下にどんどん長くなり、非常に見ずらいものになります。
そこで、汎用性のあるページャーを作ってみました。

-----2015.02.06(修正)-----
ページ数が多くなると希望のページへの移動が難しくなるので、中央部のページを10件単位でプラスマイナスできるように修正しました。
後、不要なコードなどもありましたので整理しました。

-----2015.06.21(修正)-----
イベント登録のページ番号クリックで、先頭ページと最終ページをクリックした場合、中央のページ番号をそれぞれクリックした側に寄せられるように 修正しました。例えば、先頭ページ(1ページ)の場合はクリックしたら中央ページ番号が2ページから始まるように。(イベント登録 186行~199行追加)

MkenPagerのダウンロード(mkenPager.js)
mkenPager.zip

ページャー用関数を準備

最初にページャー用の関数を準備します。この関数にイベントやスタイルなどを追加していきます。


var MkenPager = function(tagId, totalRow, viewCount){
	// ページャーの表示されるタグ
	this.tagId = tagId;
	// データの総件数
	this.totalRow = Number(totalRow);
	// 1ページに表示する件数
	this.viewCount = Number(viewCount);
	//	現在選択されているページの先頭データの行番号
	this.currentRow = 1;
	// 最終ページ番号
	this.lastPageNum = Math.ceil(this.totalRow / this.viewCount);
};

引数を3個とる関数です。それぞれの引数の意味は、次のとおりです。
1. tagId:ページャーが表示されるHTMLタグのidです(例:<div id="paging"></div>) 2. totalRow:表示するデータの総件数です。 3. viewCount:1ページに表示したい件数です。

プロパティやメソッドの追加(各要素を生成)

前記の関数MkenPagerにプロパティやメソッドを追加していきます。プロトタイプ定義をオブジェクト形式で追加しています。
まずは各要素の生成です。


MkenPager.prototype = {
createHTML: function(){
	// ページャーを囲む枠
	var htmlStr = '<div id="' + this.tagId + 'Pager">';
	// 前へボタン(子の空のdivは三角)
	htmlStr += '<div id="' + this.tagId + 'Prev"><div></div></div>';
	// マイナスボタン(中央部のページを10件マイナスする)
	htmlStr += '<div id="' + this.tagId + 'Minus">-</div>';
	// ページ表示部
	htmlStr += '<ul id="' + this.tagId + 'PageNumGroup">';
	// ページ番号を作成
	var tmpTotalRow = this.totalRow;
	// リストの数は10個で固定
	for(var pageNum = 1; pageNum <= 10; pageNum++){
		if(tmpTotalRow > 0){
			if(pageNum == 10){
				htmlStr += '<li>' + this.lastPageNum + '</li>';
			}else{
				htmlStr += '<li>' + pageNum + '</li>';
			}
		}else{
			htmlStr += '<li class="' + this.tagId + 'nonePage">&nbsp;</li>';
		}
		tmpTotalRow = tmpTotalRow - this.viewCount; 
	}
	htmlStr += '</ul>';
	// プラスボタン(中央部のページを10件プラスする)
	htmlStr += '<div id="' + this.tagId + 'Plus">+</div>';
	// 次へボタン(子の空のdivは三角)
	htmlStr += '<div id="' + this.tagId + 'Next"><div></div></div>';
	htmlStr += '</div>';
	// フロートクリア
	htmlStr += '<div id="' + this.tagId + 'clear"></div>';
	
	var pagerContainer = document.getElementById(this.tagId);
	pagerContainer.innerHTML = htmlStr;
},

プロパティやメソッドの追加(スタイルの設定)

次はスタイルの設定です。長いのでスタイルシートでとも考えましたが、ファイルの扱いも考えコードに埋め込みました。


setStyle: function(){
	// 全体を囲むdiv
	var pager = document.getElementById(this.tagId + 'Pager');
	with(pager.style){
		width = "600px";
		height = "32px";
		margin = "0px 50px 50px 50px";
		border = "1px solid rgba(0,128,128,0.5)";
		borderRadius = "15px";
	}
	// 前へボタン
	var prev = document.getElementById(this.tagId + 'Prev');
	with(prev.style){
		width = "26px";
		height = "26px";
		margin = "3px 55px 3px 17px";
		borderRadius = "13px";
		float = "left";
		backgroundColor = "#666666";
		cursor = "default";
		opacity = 0.5;
	}
	// 前へボタンの三角
	var prevChild = prev.firstChild;
	with(prevChild.style){
		width = "0px";
		height = "0px";
		borderTop = "6px solid transparent";
		borderRight = "10px solid #ffffff";
		borderBottom = "6px solid transparent";
		borderLeft = "10px solid transparent";
		margin = "7px 0 0 -3px";
	}
	// 次へボタン
	var next = document.getElementById(this.tagId + 'Next');
	with(next.style){
		width = "26px";
		height = "26px";
		margin = "3px 17px 3px 55px";
		borderRadius = "13px";
		float = "left";
		backgroundColor = "#666666";
		cursor = "pointer";
	}
	// 次へボタンの三角
	var nextChild = next.firstChild;
	with(nextChild.style){
		width = "0px";
		height = "0px";
		borderTop = "6px solid transparent";
		borderRight = "10px solid transparent";
		borderBottom = "6px solid transparent";
		borderLeft = "10px solid #ffffff";
		margin = "7px 0 0 9px";
	}
	// マイナスボタン
	var minus = document.getElementById(this.tagId + 'Minus');
	with(minus.style){
		width = "20px";
		height = "20px";
		margin = "5px 0px";
		float = "left";
		textAlign = "center";
		color = "rgba(0,102,153,0.5)";
		fontWeight = "bold";
		fontSize = "15px";
		border = "1px solid rgba(0,128,128,0.5)";
		borderRadius = "10px";
		cursor = "default";
}
	// プラスボタン
	var plus = document.getElementById(this.tagId + 'Plus');
	with(plus.style){
		width = "20px";
		height = "20px";
		margin = "5px 0px";
		float = "left";
		textAlign = "center";
		color = "rgba(0,102,153,0.5)";
		fontWeight = "bold";
		fontSize = "15px";
		border = "1px solid rgba(0,128,128,0.5)";
		borderRadius = "10px";
		if(this.lastPageNum < 11){
			cursor = "default";
		}else{
			cursor = "pointer";
		}
	}
	// 中央部のUL(ページ番号のコンテナ)
	var pageNumGroup = document.getElementById(this.tagId + "PageNumGroup");
	with(pageNumGroup.style){
		width = "360px";
		height = "32px";
		textAlign = "center";
		fontSize = "12px";
		float = "left";
	}
	// ul内のli (ページ番号部)
	var nodes = pageNumGroup.childNodes;
	for(var i = 0; i < nodes.length; i++){
		with(nodes[i].style){
			display = "inline-block";
			width = "23px";
			height = "20px";
			margin = "4px 1px";
			padding = "2px 1px 0px 1px";
			border = "1px solid rgba(0,128,128,0.5)";
			color = "#006699";
			if(i == 0){
				cursor = "default";
				backgroundColor = "rgba(130,210,253,0.5)";
			}else{
				cursor = "pointer";
				backgroundColor = "rgba(130,210,253,0.1)";
			}
		}
	}
	// ページ数が10件を超えたら1ページ目と最終ページを外側にずらす
	if(this.lastPageNum > 10){
		nodes[0].style.marginRight = "15px";
		nodes[9].style.marginLeft = "15px";
	}
	// ページが無い部分のスタイル
	var nonePage = document.getElementsByClassName(this.tagId + "nonePage");
	for(var i = 0; i < nonePage.length; i++){
		with(nonePage[i].style){
			borderColor = "#eeeeee";
			cursor = "default";
		}
	}
	// フロートクリア用div セレクタAPIを使用
	var floatClear = document.querySelector("#" + this.tagId + "clear");
	with(floatClear.style){
		width = 0;
		height = 0;
		display = "none";
		clear = "both";
	}
},

プロパティやメソッドの追加(イベント登録)

いよいよ難関のイベントです。この部分に何日もかかってしまいました。


setEvent: function(){
	// イベントのthisはインスタンスではなくイベントの発生元を指すので
	// インスタンスは一旦ローカル変数に代入して使用する
	var self = this;
	var lastIdx = Math.floor(self.totalRow / self.viewCount);
	// ページ番号用リストの最後のIndex
	if(lastIdx > 9){lastIdx = 9;}
	// 現在選択されているページ番号用リスト(0~9)
	var currentIdx = 0;
	// 現在選択されているページ番号
	var currentPageNum = 1;
	var prev = document.getElementById(this.tagId + 'Prev');
	var next = document.getElementById(this.tagId + 'Next');
	var minus = document.getElementById(this.tagId + 'Minus');
	var plus = document.getElementById(this.tagId + 'Plus');
	var pageNumGroup = document.getElementById(this.tagId + "PageNumGroup");

	var nodes = pageNumGroup.childNodes;
	// ページ番号クリック **********
	for(var i = 0; i < nodes.length; i++){
		this.addListener(nodes[i], "click", function(){
			// クリックされたインデックスを取得
			var count = 0;
			var node = this;
			// 直前の兄弟がある限りカウンターを増やすことで自分が何番目かがわかる
			while(node.previousSibling) {
				++count;
				node = node.previousSibling;
			}
			
			var oldIdx = currentIdx;	// 直前のliインデックス
			currentIdx = count;

			// 先頭ページの場合、中央のページ番号部分を先頭ページ側に寄せる
			if(currentIdx == 0){
				for(var j = 1; j <= 8; j++){
					nodes[j].childNodes[0].nodeValue = j + 1;
				}
			}
			// 最終ページの場合、中央のページ番号部分を最終ページ側に寄せる
			if(currentIdx == 9){
				var k = 8;
				for(var j = 1; j <= 8; j++){
					nodes[j].childNodes[0].nodeValue = self.lastPageNum - k;
					k--;
				}
			}

			// ページ番号のstyleを元に戻す
			defaultPageStyle(nodes[oldIdx]);
			// クリックされたページ番号のstyleを変える
			newPageStyle(this);

			// 現在のページ番号を取得
			getPageNum(this);
			// 先頭データ行番号を取得
			getTopRowNum();
			// ボタンスタイル変更
			btnStyle(prev, 0);
			btnStyle(next, lastIdx);
		});
	}

	// 前へボタンクリック **********
	this.addListener(prev, 'click', function(){
		if(currentIdx == 0){	// 先頭ページなら何もしない
			return;
		}else{
			// ページ番号のstyleを元に戻す
			defaultPageStyle(nodes[currentIdx]);
			// 最初と2番目リストのページ差が2ページ以上ある場合は
			// 最初と最後を除いた中央部のリストのページ番号を減らす
			if((currentIdx == 1) && ((currentPageNum - 1) >= 2)){
				for(var i = 1; i <= 8; i++){
					var pageNum = Number(nodes[i].childNodes[0].nodeValue);
					nodes[i].childNodes[0].nodeValue = pageNum - 1;
				}
			}else{
				// インデックスを減らす
				currentIdx--;
			}
			// ページ番号の新スタイル
			newPageStyle(nodes[currentIdx]);
			// 現在のページ番号を取得
			getPageNum(nodes[currentIdx]);
			// 先頭データ行番号を取得
			getTopRowNum();
		}
		// ボタンスタイル変更
		btnStyle(prev, 0);
		next.style.cursor = "pointer";
		next.style.opacity = 1;
		// プラスマイナスのカーソル変更
		plusMinusStyle();
	});

	// 次へボタンクリック **********
	this.addListener(next, 'click', function(){
		if(currentIdx == lastIdx){	// 最終ページなら何もしない
			return;
		}else{
			// ページ番号のstyleを元に戻す
			defaultPageStyle(nodes[currentIdx]);
			// 最初と最後を除いたリストのページ番号を増やす
			//(最後とその前のリストのページ差が2ページ以上の場合)
			if((currentIdx == 8) && ((self.lastPageNum - currentPageNum) >= 2)){
				for(var i = 1; i <= 8; i++){
					var pageNum = Number(nodes[i].childNodes[0].nodeValue);
					nodes[i].childNodes[0].nodeValue = pageNum + 1;
				}
			}else{
				// インデックスを増やす
				currentIdx++;
			}
			// ページ番号の新スタイル
			newPageStyle(nodes[currentIdx]);
			// 現在のページ番号を取得
			getPageNum(nodes[currentIdx]);
			// 先頭データ行番号を取得
			getTopRowNum();
		}
		// ボタンスタイル変更
		btnStyle(next, lastIdx);
		prev.style.cursor = "pointer";
		prev.style.opacity = 1;
		// プラスマイナスのカーソル変更
		plusMinusStyle();
	});

	// マイナスボタンクリック **********
	this.addListener(minus, 'click', function(){
		var node1Val = Number(nodes[1].childNodes[0].nodeValue);
		// 2番目のページ番号から10を差し引いた結果が2ページ未満の場合
		if((node1Val - 10) < 2){
			// 3ページから11ページならば中央部のページ番号を1ページ側にずらす
			if(node1Val > 2 && node1Val < (2 + 10)){
				for(var i = 1; i <= 8; i++){
					var pageNum = Number(nodes[i].childNodes[0].nodeValue);
					nodes[i].childNodes[0].nodeValue = pageNum - (node1Val - 2);
				}
			// それ以外は何もしない
			}else{
				return;
			}
		}else{
			// 最初と最後を除いた中央部のリストのページ番号を10づつ減らす
			for(var i = 1; i <= 8; i++){
				var pageNum = Number(nodes[i].childNodes[0].nodeValue);
				nodes[i].childNodes[0].nodeValue = pageNum - 10;
			}
		}
		// 現在のページ番号を取得
		getPageNum(nodes[currentIdx]);
		// 先頭データ行番号を取得
		getTopRowNum();
		// プラスマイナスのカーソル変更
		plusMinusStyle();
	});

	// プラスボタンクリック **********
	this.addListener(plus, 'click', function(){
		var node8Val = Number(nodes[8].childNodes[0].nodeValue);
		// ページ数が10に満たない場合
		if(lastIdx < 9){
			return;
		}
		// 9番目のページ番号に10を足した結果が最後から1ページ前のページ番号を超える場合
		if((node8Val + 10) > (self.lastPageNum - 1)){
			// 9番目のページ番号が(最終ページ番号-10)から(最終ページ-2)の間ならば
			// 中央部のページ番号を最終ページ側にずらす
			if(node8Val >= (self.lastPageNum - 10)
				&& node8Val <= (self.lastPageNum -2)){
				for(var i = 1; i <= 8; i++){
					var pageNum = Number(nodes[i].childNodes[0].nodeValue);
					nodes[i].childNodes[0].nodeValue = 
						pageNum + ((self.lastPageNum - 1) - node8Val);
				}
			// それ以外は何もしない
			}else{
				return;
			}
		}else{
			// 最初と最後を除いた中央部のリストのページ番号を10づつ増やす
			for(var i = 1; i <= 8; i++){
				var pageNum = Number(nodes[i].childNodes[0].nodeValue);
				nodes[i].childNodes[0].nodeValue = pageNum + 10;
			}
		}
		// 現在のページ番号を取得
		getPageNum(nodes[currentIdx]);
		// 先頭データ行番号を取得
		getTopRowNum();
		// プラスマイナスのカーソル変更
		plusMinusStyle();
	});

	//	関数:既定のページ番号のスタイル
	function defaultPageStyle(node) {
		node.style.cursor = "pointer";
		node.style.backgroundColor = "rgba(130,210,253,0.1)";
	}
	// 関数:新たに適用するページ番号のスタイル
	function newPageStyle(node) {
		node.style.cursor = "default";
		node.style.backgroundColor = "rgba(130,210,253,0.5)";
	}
	// 関数:ボタンスタイル変更
	function btnStyle(node, nodeIdx) {
		if(currentIdx == nodeIdx){
			node.style.cursor = "default";
			node.style.opacity = 0.5;
		}else{
			node.style.cursor = "pointer";
			node.style.opacity = 1;
		}
	}
	// 関数:プラスマイナスのカーソル変更
	function plusMinusStyle(){
		var node1 = Number(nodes[1].childNodes[0].nodeValue);
		var node8 = Number(nodes[8].childNodes[0].nodeValue);
		var node9 = Number(nodes[9].childNodes[0].nodeValue);
		if(node1 - 1 == 1){
			minus.style.cursor = "default";
		}else{
			minus.style.cursor = "pointer";
		}
		if(node9 - node8 == 1){
			plus.style.cursor = "default";
		}else{
			plus.style.cursor = "pointer";
		}
	}
	// 関数:現在のページ番号を取得
	function getPageNum(node) {
		var txtNode = node.childNodes[0];
		currentPageNum = Number(txtNode.nodeValue);
	}
	// 関数:先頭データ行番号を取得
	function getTopRowNum() {
		self.currentRow = 1 + (currentPageNum - 1) * self.viewCount;
	}
},

プロパティやメソッドの追加(イベントのクロスブラウザ対策)

おきまりのIE対策。


addListener: function(elm, ev, listener){
	if(elm.addEventListener) {		// IE以外
		elm.addEventListener(ev, listener, false);
	} else if(elm.attachEvent) {	// IE
		elm.attachEvent('on' + ev, listener);
	} else {
		throw new Error('イベントリスナーに未対応です。');
	}
},

プロパティやメソッドの追加(ページャー表示用メソッド)

いままでのメソッドを集めて、ひとつのページャー表示用メソッドにしてあります。
インスタンスの後にこのメソッドを呼び出してページャーを使用します。


showPager: function(){
	this.createHTML(); // 各要素を生成
	this.setStyle();   // スタイルをセット
	this.setEvent();   // イベントをセット
}

ページャーの使い方

このページのページャーサンプルではidがpagingのdivに表示させています。
データの件数は100件、1ページの表示件数は7件で呼び出ししています。


<head>
	<meta charset="UTF-8">
	<title>団塊爺ちゃんの備忘録</title>
	<link rel="stylesheet" href="index.css">
	<script src="jquery-1.8.0.min.js"></script>
	<script src="mkenPager.js"></script>
	<script src="index.js"></script>
</head>
<body>
	<div id="paging"></div> <!--ページャーの表示場所-->

var tagId = "paging";
// データの総件数が100件で1ページに7件づつ表示の場合
var pager = new MkenPager(tagId, 100, 7);
// ページャーの表示
pager.showPager();

// ここからページャー用イベント
// 各ページをクリック
$("#" + tagId + " li").on("click", function(){
	action();
});
// 「前へ」ボタンクリック
$("#" + tagId + "Prev").on("click", function(){
	action();
});
// 「次へ」ボタンクリック
$("#" + tagId + "Next").on("click", function(){
	action();
});
// マイナスボタンクリック
$("#" + tagId + "Minus").on("click", function(){
	action();
});
// プラスボタンクリック
$("#" + tagId + "Plus").on("click", function(){
	action();
});
function action(){
	var msg = 'このページの先頭の行番号は'+pager.currentRow +'行目です。';
	alert(msg);
}

7行目から下がページャーのイベントで、jQueryでイベントを追加しています。
アプリ側で必要なデータはページャーの各プロパティから取得します。
ページの先頭の行番号:pager.currentRow
などです。