Paginator: Manually rendering Paginator UI Components

タイトルのManually Renderingとは何を指すのか、と思ってしまうが、このExampleの説明は以下のように書かれている。

If you have a UI where it doesn't make sense to place all controls in a single container (or set of containers), you can place individual UI Components manually outside Paginator's configured container(s).(YUIのExampleページから引用)
もし、コンテナに(Paginatorの)全てのコントロールを置く場所がないなら、Paginatorの個々のコンポーネントをコンテナの外で利用することができる。

つまり、Paginatorの「部品取り」をしようというサンプルである。画面は下。

この表では、「ページの遷移状況の監督」にはPaginatorが使われているが、表そのものは、完全に手作りとなっている。
この表を管理する(手作りの)関数系と、Paginatorを、イベントハンドラ内で同期して利用することで、カスタマイズされた表ができあがっている。
上の説明にもあるように、ヘッダー部はPaginatorのuiコンポーネントが用いられているが、フッターは、手作りの表の中に、Paginatorのコンポーネントが埋め込まれている。

以下に、HTMLの全文を示す。
殆ど手作りなので、意外とすっきり読めてしまうから不思議だ。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html> 
<head> 
    <meta http-equiv="content-type" content="text/html; charset=utf-8"> 
<title>Ajax Sample</title> 
 
<style type="text/css"> 
body {
	margin:0;
	padding:0;
}
</style> 

<link rel="stylesheet" type="text/css" href="scripts/yui/fonts/fonts-min.css" />
<link rel="stylesheet" type="text/css" href="scripts/yui/paginator/assets/skins/sam/paginator.css" />
<script type="text/javascript" src="scripts/yui/yahoo-dom-event/yahoo-dom-event.js"></script>
<script type="text/javascript" src="scripts/yui/element/element-beta-min.js"></script>
<script type="text/javascript" src="scripts/yui/paginator/paginator-min.js"></script>



<style type="text/css" id="defaultstyle">
#main {
	margin: 2px;
	padding: 3px;
	width: 450px;
	height: auto;
}

/* デフォルトスキン(Sam)のカスタマイズ */
.yui-skin-sam .yui-pg-container { 
/*	margin: 0; */
}
.yui-skin-sam .yui-pg-current   { 
	margin-right: 15px; 
}
.yui-skin-sam .yui-pg-previous {
    float: left;
    padding: 3px 5px;
}
.yui-skin-sam .yui-pg-next {
    float: right;
    padding: 3px 5px;
}
.yui-skin-sam span.yui-pg-next,
.yui-skin-sam span.yui-pg-previous {
    display: none;
}
#tbl,
#report,
#paging {
    width: 400px;
/*    margin: 0 auto;*/
}
#report {
    color: #fff;
    background: #ccc;
    font-size: 200%;
    margin-bottom: 1em;
    text-align: right;
}
#demo table {
    border-collapse: collapse;
    color: #333;
    width: 100%;
}
#demo th {
    border-bottom: 4px solid #999;
    color: #444;
    font: normal 125%/100% Trebuchet MS, Arial, sans-serif;
    padding: 0 6px;
}
#demo tbody {
    background: #fff;
    border-left: 1px solid #ccc;
    border-right: 1px solid #ccc;
}
#demo tbody td {
    border-bottom: 1px solid #eee;
    padding: 5px;
}
#demo tfoot td {
    overflow: hidden;
}
</style>

<script type="text/javascript" src="scripts/yui/paginator/assets/inventory.js"></script>
<script type="text/javascript"> 
YAHOO.util.Event.onDOMReady(function () {
	 
	// YAHOO.exampleというNameSpaceを定義
	var Ex = YAHOO.namespace('example');
	var d  = document;

	// DOMをハンドルするためのオブジェクト・リテラルの定義
	Ex.DOM = {
		// HTMLElementをもらって、そのinnerHTMLに文言を仕込む
	    create : function (el,innerHTML) {
	        el = el && el.nodeName ? el : d.createElement(el);

	        if (el && innerHTML !== undefined) {
	            el.innerHTML = innerHTML;
	        }

	        return el;
	    },
		// HTMLElementをもらって、子要素を作成し、そのinnerHTMLに文言を仕込む
	    add : function (par, child, innerHTML) {
	        par = par && YAHOO.util.Dom.get(par);
	        if (par && par.appendChild) {
	            child = Ex.DOM.create(child,innerHTML);
	            if (child) {
	                par.appendChild(child);
	            }
	        }

	        return child;
	    }
	};

	// Table作成するために主要な役割を担う関数群(オブジェクト・リテラル)
	Ex.table = {
	    table   : null,
	    columns : ['Item','Quantity','Description'],
	    pageSize: 5,
	    data    : null,
	    // 表示する1ページ全体を配列にもつ。(ページ数分だけの要素がある)
	    tbody   : [],
	    tfoot   : null,

		// コンストラクタ的な役割をもつ
	    load : function (data) {
	        if (YAHOO.lang.isArray(data)) {
	            this.data = data;
	            this.tbody = [];
	        }
	        return this;
	    },

		// テーブルを作成する
	    render : function (container) {
		    // Tableが空なら作成・表示する。
	        if (!this.table) {
	            container = (container && YAHOO.util.Dom.get(container)) || d.body;

	            var thead, tbody, row, cell, i, len;

	            this.table = Ex.DOM.create('table');
	            thead = Ex.DOM.add(this.table,'thead');
	            row   = Ex.DOM.add(thead,'tr');

	            for (i=0,len=this.columns.length; i<len; ++i) {
	                Ex.DOM.add(row,'th',this.columns[i]);
	            }

	            this.tfoot = Ex.DOM.add(this.table,'tfoot');
	            cell = Ex.DOM.add(Ex.DOM.add(this.tfoot,'tr'),'td');
	            cell.colSpan = this.columns.length;

	            // Dataがあれば1ページ目を表示し、なければ、'データがありません'と表示する。
	            if (this.data) {
	                this.showPage(1);
	            } else {
	                row  = Ex.DOM.create('tr');
	                cell = Ex.DOM.add(row,'td','データがありません');
	                cell.colSpan = this.columns.length;

	                Ex.DOM.add(Ex.DOM.add(this.table,'tbody'),row);
	            }

	            container.innerHTML = '';
	            Ex.DOM.add(container,this.table);
	        }
	        return this;
	    },

	    // 指定されたページを表示する
	    showPage : function (page) {
	        var cur, tbody, row, i, j, len, limit;

	        if (this.table) {
		        // 現在のページを取得。
	            cur = this.table.getElementsByTagName('tbody')[0];

	            if (YAHOO.lang.isNumber(page)) {
	                tbody = this.tbody[page];
					// TBody(テーブルの表示部)が空か、指定されたページ(this.tbody[page])と違うなら、
					// テーブルを作成する。
	                if (!cur || cur !== tbody) {
	                    if (!tbody) {
	                        tbody = this.tbody[page] = Ex.DOM.create('tbody');
							// 指定されたページ番号を元に地道にテーブルを作成
	                        i = (page - 1) * this.pageSize;
	                        limit  = Math.min(Ex.data.inventory.length,
	                                          i + this.pageSize);
	                        for (; i < limit; ++i) {
	                            row = Ex.DOM.add(tbody,'tr');
	                            for (j=0,len=this.columns.length; j<len; ++j) {
	                                Ex.DOM.add(row,'td',
	                                    this.data[i][this.columns[j]]);
	                            }
	                        }
	                    }

						// 現在の表示ページがnullでないなら、算出したテーブルでreplace、nullなら
						// tableにbody要素を加える。
	                    if (cur) {
	                        this.table.replaceChild(tbody,cur);
	                    } else {
	                        Ex.DOM.add(this.table,tbody);
	                    }
	                }
	            }
	        }
	        return this;
	    }
	};

	// Paginatorで「ページを表示せよ」というイベントが発火したときのハンドラ
	Ex.handlePagination = function (state) {
		// 上で定義した、tableオブジェクトでページを表示
	    Ex.table.showPage(state.page);
	    // Paginatorのステートを(指定されたページに)かえる。
	    Ex.paginator.setState(state);
	};

	// Paginatorのインスタンス化
	Ex.paginator = new YAHOO.widget.Paginator({
	    rowsPerPage  : Ex.table.pageSize,
	    totalRecords : Ex.data.inventory.length,
		// コンテナは適当に作る(これでもいいことを例示しているだけ)
	    containers   : d.createElement('div'),
		// paginatorでテンプレートを利用(UIコンポーネント)。
	    template              : "{PreviousPageLink}{NextPageLink}",
	    pageReportTemplate    : "Page {currentPage} of {totalPages}",
	    previousPageLinkLabel : "previous",
	    nextPageLinkLabel     : "next"
	});

	Ex.paginator.subscribe('changeRequest', 
			Ex.handlePagination);
	
	Ex.paginator.subscribe('render', function () {
	    var pageReport, pageReportNode, report;

	    report = YAHOO.util.Dom.get('report');

	    // Paginator.ui.CurrentPageReportのインスタンス化(ページ;n/n形式)
	    // http://developer.yahoo.com/yui/docs/YAHOO.widget.Paginator.ui.CurrentPageReport.html
	    pageReport = new YAHOO.widget.Paginator.ui.CurrentPageReport(Ex.paginator);

	    // Paginator.ui.CurrentPageReportのレンダリング。
	    // 引数は任意でいいが、普通は、ターゲットとなるコンテナのIDを指定。
	    pageReportNode = pageReport.render('report');

		// ターゲット(report)の子要素として、レンダリングしたCurrentPageReportのインスタンスを入れる。
	    report.appendChild(pageReportNode);
	});

	// Tableオブジェクトのload
	Ex.table.load(Ex.data.inventory).render('tbl');

	// Paginatorのレンダリング
	Ex.paginator.render();

	// TableのFooterにPaginaterのコンポーネントを使う。
	Ex.DOM.add(Ex.table.tfoot.rows[0].cells[0],
			Ex.paginator.getContainerNodes()[0]);
	
});
</script>
</head> 
 
<body class=" yui-skin-sam">
<div id="main">
<p>表部分と表現形式(の殆ど)はJasvscript、遷移にYUI.widget.Paginatorを利用したサンプルです。
</p>
<div id="demo">
    <div id="report"></div>
    <div id="tbl"></div>
</div> 
</div>
</body> 
</html>