Menu Family: Website Top Nav Using Animation With Submenus Built From Markup

この例は、「Menu Family: Website Top Nav With Submenus Built From Markup」と同様にメニューバーからサブメニューが開くものだが、1点だけ

  • サブメニューがメニューバーからスライドして降りてくる

という工夫を施してある。

この工夫を実装するにあたり、YUIのAnimation機能を使っている。

サンプリングの初期画面は以下。

YUIのExampleでは、サブメニューが開く際に発火するイベントにハンドラーを仕込んで、トップメニューからサブメニューが開くときだけ、スライドイン機能が働くような仕掛けとなっている。
サンプリングでは、ハンドラーの発火タイミングをみる目的で、画面の「本文」用のフィールドにログを吐き出させてみた。
それが以下の画面である。(サブメニューは閉じている)

以下にJavascriptを含むhtlmの全文を示す。
これを見ると、

  • とても古典的な(style)手法でAnimationを実装していること(サブメニューの隠し方と出し方)。
  • User Agentの補正を実装していること
  • これらのために(簡単なスライドインの実装なのに)コード量が非常に増えていること。
  • Website Left Nav Using Animation With Submenus Built From Markup」でサンプリングした簡単なEffect(FadeIN/Out)と比較して、コード量が非常に多く可読性が悪いこと。

が分かる。
2,3は1に起因するものであるが、Javascriptライブラリー点な面から論じてしまうと、意見がでそうなところだ。

<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>
<!-- ヘッダー部、本文(コンテンツ部、メニュー部)、フッター部を分けるグリッド表示のCSS -->
<link rel="stylesheet" type="text/css" href="scripts/yui/reset-fonts-grids/reset-fonts-grids.css">
 
<link rel="stylesheet" type="text/css" href="scripts/yui/menu/assets/skins/sam/menu.css" />
<script type="text/javascript" src="scripts/yui/yahoo-dom-event/yahoo-dom-event.js"></script>
<script type="text/javascript" src="scripts/yui/container/container_core-min.js"></script>
<script type="text/javascript" src="scripts/yui/menu/menu-min.js"></script>
<script type="text/javascript" src="scripts/yui/animation/animation.js"></script>

<style type="text/css" id="defaultstyle">

div.yui-b p {
    margin: 0 0 .5em 0;
    color: #999;
}
            
div.yui-b p strong {
    font-weight: bold;
    color: #000;
}
            
div.yui-b p em {
    color: #000;
}            

h1 {
   font-weight: bold;
   margin: 0 0 1em 0;
   padding: .25em .5em;
   background-color: #ccc;
}

/* メニューのbottom marginを設定 */
#productsandservices {
   margin: 0 0 10px 0;
}

</style>

<script type="text/javascript">

//モジュールパターンで実装する。
YAHOO.namespace("EGP");

YAHOO.EGP.MarkupMenu = function() {

	var oMenu;
        var ua = YAHOO.env.ua,
    	oAnim;  // Animation instance
		Dom = YAHOO.util.Dom;

	/*
    	SubMenuが開く前のイベントのハンドラ; 
		thisは<ul>エレメント。
    	サブメニューがAnimationされる前のスタイルの調整を行う。
		<ul>エレメント(サブメニュー)のアニメーションを動かす。
	*/

	var onSubmenuBeforeShow = function (p_sType, p_sArgs) {

   		var oBody,
       		oElement,
     		oShadow,
       		oUL;

   		
		//ログの表示
		if(this instanceof YAHOO.widget.MenuBarItem){
			var nInst = 'YAHOO.widget.MenuBarItem';
		} else if(this instanceof YAHOO.widget.MenuItem){
			nInst = 'YAHOO.widget.MenuItem';
		} else {
			nInst = 'unknown';
		}
        var log = 	'<hr/><font color="red"> owner: ' + this.cfg.toString() + 
					' instance of :' + nInst + 
        			' (in onSubmenuBeforeShow) ' +  '</font><br/>';
					Dom.get('log').innerHTML += log;
		
   		if (this.parent) {
			// subメニュー(ul以下のDOM)の保管
   			oElement = this.element;

	       	/*
    	        Get a reference to the Menu's shadow element and 
        	    set its "height" property to "0px" to syncronize 
            	it with the height of the Menu instance.
       		*/

			// これで陰影が取得できる??
       		oShadow = oElement.lastChild;
       		// 陰影を隠す。
       		oShadow.style.height = "0px";
       
	       /*
    	       Stop the Animation instance if it is currently 
        	   animating a Menu.
       		*/ 
   
       		if (oAnim && oAnim.isAnimated()) {
           		oAnim.stop();
           		oAnim = null;
       		}

	       /*
    	       Set the body element's "overflow" property to 
        	   "hidden" to clip the display of its negatively 
           		positioned <ul> element.
       		*/ 
			// class="bd" を保存
       		oBody = this.body;


           //  Check if the menu is a submenu of a submenu.
	       //  Submenu の SubMenuの時の処理(User Agentのごとの調整)
    	   if (this.parent && 
        	   !(this.parent instanceof YAHOO.widget.MenuBarItem)) {

           		/*
               There is a bug in gecko-based browsers and Opera where 
               an element whose "position" property is set to 
               "absolute" and "overflow" property is set to 
               "hidden" will not render at the correct width when
               its offsetParent's "position" property is also 
               set to "absolute."  It is possible to work around 
               this bug by specifying a value for the width 
               property in addition to overflow.
           		*/

		        if (ua.gecko || ua.opera) {
        	 		oBody.style.width = oBody.clientWidth + "px";
           		}
           
		        /*
        	    Set a width on the submenu to prevent its 
            	width from growing when the animation 
               	is complete.
           		*/
           		if (ua.ie == 7) {
               		oElement.style.width = oElement.clientWidth + "px";
           		}
	       }
			// class="bd" のスタイル
       		oBody.style.overflow = "hidden";

       		/*
           		Set the <ul> element's "marginTop" property 
           		to a negative value so that the Menu's height
           		collapses.
       		*/
       		
			// class="bd" 内のulをとる。
       		oUL = oBody.getElementsByTagName("ul")[0];
           	// marginTopをマイナスにしてサブメニューを隠してしまう。(※)
       		oUL.style.marginTop = ("-" + oUL.offsetHeight + "px");
   		}

	}

	/*
	Anmationの間の処理;
		ieのときしか動かない。
	*/
	var onTween = function (p_sType, p_aArgs, p_oShadow) {

		//ログの表示
		if(this instanceof YAHOO.widget.MenuBarItem){
			var nInst = 'YAHOO.widget.MenuBarItem';
		} else if(this instanceof YAHOO.widget.MenuItem){
			nInst = 'YAHOO.widget.MenuItem';
		} else {
			nInst = 'unknown';
		}
		var log = 	'<hr/><font color="blue"> owner: ' + this.cfg.toString() +
		' instance of :' + nInst + 
		' (in onTween) ' +  '</font><br/>';
		Dom.get('log').innerHTML += log;

		if (this.cfg.getProperty("iframe")) {
    		this.syncIframe();
		}

		if (p_oShadow) {
    		p_oShadow.style.height = this.element.offsetHeight + "px";
		}
	}

	/*
	SubMenuが開くときのイベントのハンドラ; 
		thisは<ul>エレメント。
		<ul>エレメント(サブメニュー)のアニメーションを動かす。
	*/

	var onSubmenuShow = function (p_sType, p_sArgs) {

		var oElement,
   		 	oShadow,
   			oUL;
		var	obj = this;

		//ログの表示
		if(obj instanceof YAHOO.widget.MenuBarItem){
			var nInst = 'YAHOO.widget.MenuBarItem';
		} else if(obj instanceof YAHOO.widget.MenuItem){
			nInst = 'YAHOO.widget.MenuItem';
		} else {
			nInst = 'unknown';
		}

		obj = this.parent;
		if(obj instanceof YAHOO.widget.MenuBarItem){
			var pInst = 'YAHOO.widget.MenuBarItem';
		} else if(obj instanceof YAHOO.widget.MenuItem){
			pInst = 'YAHOO.widget.MenuItem';
		} else {
			pInst = 'unknown';
		}
		
        var log = 	'<hr/> owner: ' + this.cfg.toString() + 
		' instance of :' + nInst + 
		' instance(parent) of :' + pInst + 
                ' (in onSubmenuShow) ' +  '<br/>';
		Dom.get('log').innerHTML += log;

		if (this.parent) {
   			oElement = this.element;
   			oShadow = oElement.lastChild;
			// class=bdの取得。
   			oUL = this.body.getElementsByTagName("ul")[0];

           	        // marginTopをマイナスにして隠してしまったもの0に戻すことで、現れる。
   			oAnim = new YAHOO.util.Anim(oUL, 
       					{ marginTop: { to: 0 } },
       					.5, YAHOO.util.Easing.easeOut);

			// フレームの陰影の高さを100%にする。
        	        oAnim.onStart.subscribe(function () {
    	   			oShadow.style.height = "100%";
   			});
   			oAnim.animate();

   			/*
       			Subscribe to the Anim instance's "tween" event for 
       			IE to syncronize the size and position of a 
       			submenu's shadow and iframe shim (if it exists)  
       			with its changing height.
   			*/

   			if (YAHOO.env.ua.ie) {
       			oShadow.style.height = oElement.offsetHeight + "px";
       			/*
           			Subscribe to the Anim instance's "tween"
           			event, passing a reference Menu's shadow 
           			element and making the scope of the event 
           			listener the Menu instance.
       			*/
       			oAnim.onTween.subscribe(onTween, oShadow, this);
   			}

   			/*
       			Subscribe to the Anim instance's "complete" event,
       			passing a reference Menu's shadow element and making 
       			the scope of the event listener the Menu instance.
   			*/
   			oAnim.onComplete.subscribe(onAnimationComplete, 
   		   				oShadow, this);
   		}

	}

	return{
    	   init: function() {
            /*
		    Menuのインスタンス化;第1引数はMenu部を意味するDivのid.
		          第2引数はConfigプロパティー。
	        */
           oMenuBar = new YAHOO.widget.MenuBar("productsandservices", 
					// Menuのコンフィグのためのオブジェクトリテラル
                   { 
                    // MouseOverでsubmenuが展開される。
                    autosubmenudisplay: true, 
                    // Menuが消えるまでの時間(msec)
                    hidedelay:  750,
                    // 表示するときまで、レンダリングを待つ。
                    lazyload: true 
                    }
           );

           // subMenuが開く直前に発火
           oMenuBar.subscribe("beforeShow", onSubmenuBeforeShow);
           // subMenuが開く際に発火
           oMenuBar.subscribe("show", onSubmenuShow);

           // stataic Menu(CSSで規定されたメニュー)なので、renderだけで、
           // showはいらない
           oMenuBar.render();            
   		
		}
	};
}();

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

</script>
</HEAD>

<body class="yui-skin-sam">
   <div id="doc" class="yui-t1">
      <div>
          <!-- start: your content here -->
          <p>ヘッダー:ここがヘッダーです。</p>
          <h1>Example: Website Top Nav With Submenus Built From Markup (YUI Library)</h1>
          <!-- end: your content here -->
      </div>

      <div id="bd">
         <div id="yui-main">
			<!-- 1コラム目のスタート(メニューバー、とコンテンツ) -->
             <div class="yui-b">
                 <!-- start: stack grids here -->
					<!-- メニュー部 -->
                    <div id="productsandservices" class="yuimenubarnav">
                         <div class="bd">
                              <ul>
                                    <li class="yuimenubaritem"><a href="#communication">Communication</a>
                                        <div id="communication">
                                            <div class="bd">
                                                <ul>
                                                    <li class="yuimenuitem"><a href="http://360.yahoo.com">360&#176;</a></li>
                                                    <li class="yuimenuitem"><a href="http://alerts.yahoo.com">Alerts</a></li>
                                                    <li class="yuimenuitem"><a href="http://avatars.yahoo.com">Avatars</a></li>
                                                    <li class="yuimenuitem"><a href="http://groups.yahoo.com">Groups</a></li>
                                                    <li class="yuimenuitem"><a href="http://promo.yahoo.com/broadband/">Internet Access</a></li>
                                                    <li class="yuimenuitem"><a href="#">PIM</a>
                                                    
                                                        <div id="pim">
                                                            <div class="bd">
                                                                <ul>
                                                                    <li class="yuimenuitem"><a href="http://mail.yahoo.com">Yahoo! Mail</a></li>

                                                                    <li class="yuimenuitem"><a href="http://addressbook.yahoo.com">Yahoo! Address Book</a></li>
                                                                    <li class="yuimenuitem"><a href="http://calendar.yahoo.com">Yahoo! Calendar</a></li>
                                                                    <li class="yuimenuitem"><a href="http://notepad.yahoo.com">Yahoo! Notepad</a></li>
                                                                </ul>            
                                                            </div>
                                                        </div>                    
                                                    
                                                    </li>
                                                    <li class="yuimenuitem"><a href="http://members.yahoo.com">Member Directory</a></li>
                                                    <li class="yuimenuitem"><a href="http://messenger.yahoo.com">Messenger</a></li>
                                                    <li class="yuimenuitem"><a href="http://mobile.yahoo.com">Mobile</a></li>
                                                    <li class="yuimenuitem"><a href="http://www.flickr.com">Flickr Photo Sharing</a></li>
                                                </ul>
                                            </div>
                                        </div>      
                                    
                                    </li>
                                    <li class="yuimenubaritem"><a href="http://shopping.yahoo.com">Shopping</a>
                                        <div id="shopping" class="yuimenu">
                                            <div class="bd">                    
                                                <ul>
                                                    <li class="yuimenuitem"><a href="http://auctions.shopping.yahoo.com">Auctions</a></li>
                                                    <li class="yuimenuitem"><a href="http://autos.yahoo.com">Autos</a></li>
                                                    <li class="yuimenuitem"><a href="http://classifieds.yahoo.com">Classifieds</a></li>
                                                    <li class="yuimenuitem"><a href="http://shopping.yahoo.com/b:Flowers%20%26%20Gifts:20146735">Flowers &#38; Gifts</a></li>
                                                    <li class="yuimenuitem"><a href="http://realestate.yahoo.com">Real Estate</a></li>
                                                    <li class="yuimenuitem"><a href="http://travel.yahoo.com">Travel</a></li>
                                                    <li class="yuimenuitem"><a href="http://wallet.yahoo.com">Wallet</a></li>
                                                    <li class="yuimenuitem"><a href="http://yp.yahoo.com">Yellow Pages</a></li>
                                                </ul>
                                            </div>
                                        </div>                    
                                    </li>

                                    <li class="yuimenubaritem"><a href="http://entertainment.yahoo.com">Entertainment</a>
                                        <div id="entertainment" class="yuimenu">
                                            <div class="bd">                    
                                                <ul>
                                                    <li class="yuimenuitem"><a href="http://fantasysports.yahoo.com">Fantasy Sports</a></li>
                                                    <li class="yuimenuitem"><a href="http://games.yahoo.com">Games</a></li>
                                                    <li class="yuimenuitem"><a href="http://www.yahooligans.com">Kids</a></li>
                                                    <li class="yuimenuitem"><a href="http://music.yahoo.com">Music</a></li>
                                                    <li class="yuimenuitem"><a href="http://movies.yahoo.com">Movies</a></li>
                                                    <li class="yuimenuitem"><a href="http://music.yahoo.com/launchcast">Radio</a></li>
                                                    <li class="yuimenuitem"><a href="http://travel.yahoo.com">Travel</a></li>
                                                    <li class="yuimenuitem"><a href="http://tv.yahoo.com">TV</a></li>
                                                </ul>                    
                                            </div>
                                        </div>                                        
                                    </li>
                                    <li class="yuimenubaritem"><a href="#information">Information</a>
                
                                        <div id="information">
                                            <div class="bd">                                        
                                                <ul>
                                                    <li class="yuimenuitem"><a href="http://downloads.yahoo.com">Downloads</a></li>
                                                    <li class="yuimenuitem"><a href="http://finance.yahoo.com">Finance</a></li>
                                                    <li class="yuimenuitem"><a href="http://health.yahoo.com">Health</a></li>
                                                    <li class="yuimenuitem"><a href="http://local.yahoo.com">Local</a></li>
                                                    <li class="yuimenuitem"><a href="http://maps.yahoo.com">Maps &#38; Directions</a></li>
                                                    <li class="yuimenuitem"><a href="http://my.yahoo.com">My Yahoo!</a></li>
                                                    <li class="yuimenuitem"><a href="http://news.yahoo.com">News</a></li>
                                                    <li class="yuimenuitem"><a href="http://search.yahoo.com">Search</a></li>
                                                    <li class="yuimenuitem"><a href="http://smallbusiness.yahoo.com">Small Business</a></li>
                                                    <li class="yuimenuitem"><a href="http://weather.yahoo.com">Weather</a></li>
                                                </ul>
                                            </div>
                                        </div>                                        
                                    </li>
                                </ul>            
                            </div>
                        </div>
                        
						<!-- コンテンツ部 -->
                        <p id="note"><strong>NOTE:</strong> <em>This example demonstrates how to add submenus to a menu bar using existing markup.</em></p>

                        <form name="example">
                            <select name="test">
                                <option value="one">One</option>
                                <option value="two">Two</option>
                                <option value="three">Three</option>                                
                            </select>
                        </form>

						<p>ここにログを出しちゃおう。</p> 
						<div id='log'></div>
                        <!-- end: stack grids here -->
                    </div>
                </div>
                <!-- 1コラム目の終わり -->

                <!-- 2コラム目 -->
                <div class="yui-b">
                    <p>左ペインです。</p>
                </div>
                <!-- 2コラム目の終わり -->
            </div>

            <div>
                <p>フッター:ここがフッターです。</p>
            </div>
        </div>

    </body>
</HTML>