TreeView Control: Custom TreeView with Check Boxes

このExample(Exampleのページへのリンク)は、「Custom TreeView with Check Boxes」というタイトルになっているが、TextNodeを拡張したTaskNode.jsというノードが紹介されている。

Taskという名前が示す通り、Tree上にWBS(Work Breakdown Structure)をそのまま表現して、タスクが終了したらチェックをいれる、ということを想定しているように思われる。
各ノードは、「終了(チェックマーク)」、「仕掛(-)」、「未着手(マークなし)」の3種類のステータスをプロパティーとして保持している。
チェックするというイベントが発生すると、その親のノードへそれが伝播し、

  • 親ノードの全てのchildrenがチェック済みなら、その親ノードもチェックとする。
  • 上記以外は仕掛りとする。

という仕組みで、ノードの状態(state)がchangeする。
これは、チェックが外されたときも同様。
このリカーシブな処理も、TaskNodeの中で行われている。
(TaskNode.jsはこちらからダウンロードできる)

ここでは、これをカスタマイズすることはせず、YUIモジュールのひとつとして使ったサンプリングを行う。
以下に示すサンプルは、Exampleのページにあるものに若干の修正を加えたものである。

初期画面は以下。Exampleでは、画面トップに「Log array of checked nodes(チェック済みのノードの配列をログに書き出す)」というリンクがあるが、これは割愛した(必要な場合には、Exampleのソースコードを参照)。

上で述べた、ノードの状態遷移を示したものが以下である。

以下にJavascriptを含む、HTMLの全文を示す。多くの処理をTaskNode.jsに移譲することで、利用時のスクリプトは簡潔なものとなっていることがわかる。Scriptの配置位置を変えるにあたって、module patternに書き直しをおこなった。

<残課題>
このタスクノードを外部から読み込んだデータから生成したり、編集・保管できるように拡張することで、立派なWBSのツールができる。

<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<TITLE>Ajax_Sampling</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/treeview/assets/skins/sam/treeview.css" />

<style type="text/css" id="defaultstyle">
#container {
	margin: 2px;
	padding: 3px;
	width: 300px;
	height: auto;
	border:1px dashed #999999;
}
#expandcontractdiv {
	border:1px solid #336600; 
	background-color:#FFFFCC; 
	margin:0 0 .5em 0; 
	padding:0.2em;
}
#treeDiv1 { 
	background: #fff 
}
/*チェックボックス(この例ではCSS Spritesは行っていない)*/
.ygtvcheck0 { 
	background: url(scripts/yui/treeview/assets/img/check/check0.gif) 0 0 no-repeat; 
	width:16px; 
	cursor:pointer 
}
.ygtvcheck1 { 
	background: url(scripts/yui/treeview/assets/img/check/check1.gif) 0 0 no-repeat; 
	width:16px; 
	cursor:pointer 
}
.ygtvcheck2 { 
	background: url(scripts/yui/treeview/assets/img/check/check2.gif) 0 0 no-repeat; 
	width:16px; 
	cursor:pointer 
}
</style>

<!-- 読み込むjs --> 
<script type="text/javascript" src="scripts/yui/yahoo-dom-event/yahoo-dom-event.js" >
</script> 
<script type="text/javascript" src="scripts/yui/treeview/treeview-min.js" >
</script>
<!-- http://developer.yahoo.com/yui/examples/treeview/assets/js/TaskNode.js
              よりダウンロードして保管 -->
<script type="text/javascript" src="scripts/yui/treeview/TaskNode.js"></script>
<script type="text/javascript">
//
//モジュールパターンで、サンプルのソースを書き直した。
// (注) Tree上でのステータスの伝播は、TaskNodeオブジェクトが行っているため
//        以下に示すコードでは、Treeの生成と、画面上部のリンクの処理のみ
//        を行っている。
//
YAHOO.namespace("EGP");

YAHOO.EGP.CheckTree = function() {
	var tree;
	var nodes = [];
	var nodeIndex;

    var buildTextNodeTree = function() {
	
		//TreeViewオブジェクトのインスタンス化。
        tree = new YAHOO.widget.TreeView("treeDiv1");

    	//トップノードとして、0から7個のノードを作る。
        for (var i = 0; i < Math.floor((Math.random()*4) + 3); i++) {
            var tmpNode 
       = new YAHOO.widget.TaskNode("label-" + i, tree.getRoot(), false);
            buildRandomTaskBranch(tmpNode);
        }

        tree.draw();
    };

    function buildRandomTaskBranch(node) {
	// 階層の深さが5になるまで、リカーシブにノードの階層を構成する。
	// 1つのノードには、0から5個の子ノードがつく。
    	if (node.depth < 5) {
		for ( var i = 0; i < Math.floor(Math.random() * 4) ; i++ ) {
			var tmpNode = 
              new YAHOO.widget.TaskNode(node.label + "-" + i, 
                           node, false);
			buildRandomTaskBranch(tmpNode);
		}
	}
    };

    function checkAll() {
        var topNodes = tree.getRoot().children;
        for(var i=0; i<topNodes.length; ++i) {
            topNodes[i].check();
        }
    };

    function uncheckAll() {
        var topNodes = tree.getRoot().children;
        for(var i=0; i<topNodes.length; ++i) {
            topNodes[i].uncheck();
        }
    };
    
    return{
    init: function() {

	//「全て開く」のハンドラ
	YAHOO.util.Event.on("expand", "click", function(e) {
		YAHOO.util.Event.preventDefault(e);
		tree.expandAll();
	});
			
	//「全て閉じる」のハンドラ
	YAHOO.util.Event.on("collapse", "click", function(e) {
		YAHOO.util.Event.preventDefault(e);
		tree.collapseAll();
	});

	//「全てチェックする」のハンドラ
	YAHOO.util.Event.on("check", "click", function(e) {
		YAHOO.util.Event.preventDefault(e);
		checkAll();
	});
		
	//「全てのチェックを外す」のハンドラ
	YAHOO.util.Event.on("uncheck", "click", function(e) {
		YAHOO.util.Event.preventDefault(e);
		uncheckAll();
	});
	    	
        buildTextNodeTree();
    	}
    };
    
}();

//DOMが完全にloadされたら、サンプルを初期化する。
YAHOO.util.Event.onDOMReady(
		 //DomReadyイベントで発火するハンドラ
		YAHOO.EGP.CheckTree.init,
		//ハンドラに渡すオブジェクト(関数)
		YAHOO.EGP.CheckTree,
		//ハンドラは、上記のオブジェクトのスコープをもつ。   
		true
);
</script>
</HEAD>

<!-- class=" yui-skin-sam"の指定が必要 -->
<BODY class=" yui-skin-sam">
<div id="container">
<p>
チェックボックス付のテキストノードのサンプルです。
</p>
<div id="expandcontractdiv">
	<a id="expand" href="#">全て展開</a>
	<a id="collapse" href="#">全て閉じる</a>

	<a id="check" href="#">全てチェック</a>
	<a id="uncheck" href="#"全て>チェックを外す</a>
	<!--
	<a id="getchecked" href="#">Log array of checked nodes</a>
	  -->
</div>
<div id="treeDiv1"></div>
</div>
</BODY>
</HTML>