Yahoo! UI Library: TreeViewで今風(いまふう)のメニューをつくるには

先の「Yahoo! UI Library: TreeViewの基本(その3)」では、自HTTPサーバー配下に、DOMファイルを置き、それをXHRで一気に取得して、TreeViewを構成してみた。

ここでは、図にのって、今風のメニューを作るための基本機能について実装してみたい。
今風というのは、

  • メニューを必要に応じて表示/非表示する。
  • メニューの内容をダイナミックに変える。
  • メニューの表示にアニメーションを取り入れる。
  • メニューによる画面の切り替えの際に、画面をリロードしない。

というのを念頭においている。

突然、「メニュー」という言葉が登場したが、これは、ここまでのTreeViewと同義である。
なぜなら、TreeViewがアンカー(aタグ)のリストから生成できることから、(target=_selfで)異なったサイトの呼び出しができることが自明だからである。

メニューをダイナミックに切り替えることで、利用ユーザごとのメニューの切り替えが可能となる。
また、話は飛ぶが、これにCSSの動的な切り替え(「Get Utility: Getting CSS Style Sheets」を参照)を加えることで、利用ユーザーごとのスキンの切り替えも可能になるだろう。

ここで示すサンプルは、これらの機能を全て実装してるわけではないが、コードを読んでみれば、このコードをカスタマイズすることで、それが可能であることがわかるはずだ。

サンプルの初期画面は以下である。

閑話休題
背景の写真は、植物の写真ばかりUpしてある私のFlickrのサイトからのリンクである。
ちなみに、花の名前はコブクザクラ。晩秋から春かけて咲く桜。

さて、上の画面の「メニュー」をクリックすると、XHRでDOMファイルを取得して、透明度を上げながらメニュー(tree)が表示される(下の図)。
ここのメニュー中にあるGoogleは、前のサンプル同様にtarget=_blankで開くようになっているが、このクリックイベントに、defaultの動きを抑止して(EventオブジェクトのpreventDefaultメソッドを使えばいい)、写真を表示しているDivにiframeを差し込むようにすることで、画面の切り替えができるはずだ(この目的で、下のHTMLでは、z-indexとpositionをいじっている)。
また、カスタムアプリケーションであれば、出力結果をこのdivに差し込めばよい。

画面の「閉じる」をクリックすることで、メニューは消えて、上の画面に戻る。

以下に、JavaScriptを含むHTMLの全ソースを示す。

<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;
}

#treeDiv1 {
	margin: 5px;
	position: relative;
	width: 350px;
	height: auto;
	opacity: 0;
	background: #C0FF3E; 
	padding:1em;
	z-index: 2;
    visibility: hidden;
}

#contents {
	top: 0px;
	left: 0pix;
	position: absolute;
	border:1px dashed #999999;
	z-index: 1;
}

/*擬似リンク(メニュー)*/
#lnk1.lnk {
	top: 0px;
	left: 0pix;
	position: absolute;
	margin: 2px;
	padding: 3px;
	width: 100px;
	color: blue;
	background: #7FFF00; 
	text-decoration: underline;
	cursor: pointer;
	z-index: 3;
    visibility: visible;
}

/*擬似リンク(閉じる)*/
#lnk2.lnk {
	top: 0px;
	left: 0pix;
	position: absolute;
	margin: 2px;
	padding: 3px;
	width: 100px;
	color: blue;
	background: #7FFF00; 
	text-decoration: underline;
	cursor: pointer;
	z-index: 3;
    visibility: hidden;
}
</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>
<script type="text/javascript" src="scripts/yui/connection/connection-min.js" >
</script> 
<script type="text/javascript" src="scripts/yui/animation/animation-min.js" >
</script> 


<script type="text/javascript">
var Event = YAHOO.util.Event,
	Get    = YAHOO.util.Get,
	Dom    = YAHOO.util.Dom,
	tree;

//module patternではうまくかけなかった。(スコープの問題??)
	ajaxHandlers = {
		
		// 受信成功時の処理
		responseSuccess:function(oj){
         	var menu = eval(oj.responseText);
         	if(menu) {
				Dom.get('treeDiv1').innerHTML = menu;
         	} else {
				alert("メニューが取得できませんでした。");
         	}
         	
 	     	YAHOO.util.Event.onAvailable('treeDiv1',
				function(){
 	     			//Treeのインスタンス化:
 		       	 	tree = new YAHOO.widget.TreeView('treeDiv1');
 	     	     	tree.draw();
 	     		}
 	     	);

 	     	// Animation
 	     	Dom.setStyle('lnk1','visibility','hidden');
 	     	var attributes = {
 	   	        opacity: { to: 0.9 }
 	   	    };
	 		// Animオブジェクトのインスタンス化
		    var anim = new YAHOO.util.Anim('treeDiv1', attributes,
    	    		1,YAHOO.util.Easing.backIn);
			anim.animate(); 	 	   		
			Dom.setStyle('treeDiv1','visibility','visible');
			Dom.setStyle('lnk2','visibility','visible');
		},

		// 受信失敗時の処理
		responseFailure:function(oj){
			alert("failure");
			alert("メニューが取得できませんでした(2)。");
		},

		// テキストを読み込む
		startRequest :function(url){
 	        YAHOO.util.Connect.asyncRequest('GET', url, 
					ajaxCallback, null);
		},

		// ツリーを閉じる
		closeTree :function(){
 	     	// Animation
			Dom.setStyle('lnk1','visibility','visible');
			Dom.setStyle('lnk2','visibility','hidden');
			
			var attributes = {
 	   	        opacity: { to: 0 }
 	   	    };
	 		// Animオブジェクトのインスタンス化
		    var anim = new YAHOO.util.Anim('treeDiv1', attributes,
   	    		1,YAHOO.util.Easing.backIn);
			anim.animate(); 	   		
		}
		
	};

	// コールバック成功/失敗時の振り分け
	ajaxCallback =
	{
		success:ajaxHandlers.responseSuccess,
		failure:ajaxHandlers.responseFailure,
		// cache: falseのしないと、HttpServerがajax_test2.xml
		// の内容をcacheしてしまう。
		cache: false,
		scope: ajaxHandlers
	};

	// 擬似リンクの処理(ツリーを作る)
	// ハンドラを定義
	lnkHandlers =
	{
		click:function(){
			ajaxHandlers.startRequest('data/ajax_menu1.txt')
		}
	}
	//擬似リンクにイベントを仕掛ける。lnk1がAvailableになってからだよ。
	YAHOO.util.Event.onAvailable('lnk1',
		function() {
			YAHOO.util.Event.on('lnk1','click',
					lnkHandlers.click);
		}
	);

	// 擬似リンクの処理(ツリーを閉じる)
	// ハンドラを定義
	lnkHandlers2 =
	{
		click:function(){
			ajaxHandlers.closeTree()
		}
	}
	//擬似リンクにイベントを仕掛ける。lnk2がAvailableになってからだよ。
	YAHOO.util.Event.onAvailable('lnk2',
		function() {
			YAHOO.util.Event.on('lnk2','click',
					lnkHandlers2.click);
		}
	);

	
</script>
</HEAD>

<!-- class=" yui-skin-sam"の指定が必要 -->
<BODY class=" yui-skin-sam">
<div id="container">
<div id='lnk1' class="lnk">メニュー</div>
<div id='lnk2' class="lnk">閉じる</div>
<div id='treeDiv1'></div>
<div id='contents'>
<img src="http://farm3.static.flickr.com/2269/2095697595_efdc8f723c.jpg?v=0" />
</div>
</div>
</BODY>
</HTML>