折たたみ式メニュー

このサンプルは、もともとThe Dynamic Duo - Cross-Browser DHTML(英語)を参考にしたものですが、当サイトでは、3階層までのフォルダ階層メニューも含め、この応用バージョンであるコンパクト開閉版なども作ってみました。

基本的に、HTMLやスタイルシートが分かる方なら、簡単に設置できると思います。
# HTMLやスタイルシートが分からないという方は、ここを見るなり、タグ辞典なりを買って勉強しましょう。

なお、もともとこのサンプルは、こうしたスライド形式の開閉型メニューは結構使いでのあるものだと思うのに、日本語のサイトでは見かけないサンプルだったので作ったものです。
#> スライド形式でない単純な開閉式のフォルダ型階層メニューも、このサンプルを少し改造することにより実現できますが、それは、スタイルシートの displayプロパティあたりを使うなどした方がより簡単に実現できるので、当サイトでは解説しません。JavaScript Tips集などを参照して下さい。

また、このサンプルは、一部、W3C DOMを使っているため、W3C DOM対応ブラウザの、IE5以上、Mozilla(NN6/7)、Opera 7以上対応です。NN4やIE4などでも動作はしますが、+ の文字が - に変わったり等の一部機能は動作しません(要するに、ただ開閉するだけです。またOpera 6では動作しません。)。

また、3階層版では、NN4にも対応していませんので注意して下さい。


このページでは、もっとも基本となる2階層版の解説をしますので、3階層版や、応用サンプルであるコンパクト開閉版については、下のリンクから、サンプル、もしくはその解説に進んで下さい。
#> なお、解説も付けているのは、3階層版のみです。コンパクト開閉版に関しては、2階層版や3階層版の解説をよく読んだ上で、ソースを直接ご覧ください。

Sample   (このページで解説している2階層版)

Sample 2 (3階層版)

Sample 3 (コンパクト開閉 - 2階層版)

Sample 4 (コンパクト開閉 - 3階層版)


Source Code

<html>
<head>
<title></title>
<style type="text/css"><!--

/* 各レイヤの背景色を必ず document の背景色に合わせる。*/
.parent { position:absolute; top:50px; left:170px; width:200px; visibility:hidden } .child { position:absolute; width:200px; background-color:#ffffff; layer-background-color:#ffffff } .item { position:absolute; top:0px; left:0px; background-color:#ffffff; layer-background-color:#ffffff } .content{ position:absolute; width:200px; background-color:#ffffff; layer-background-color:#ffffff } .togg { color:#ff0000 } /* +や-などの文字のデフォルトでの色 */ .item a { color:#804000 } /* 各項目のデフォルトの文字色 */ --></style> <script language="JavaScript1.2"><!-- /* 以下、"_ie5=(navigator.appName.indexOf('Microsoft Internet Explorer')>=0
&& document.getElementById)?true:false;"までの記述を必ず入れて下さい。*/

// _bro: 1=NN6+, 2=NN4, 3=IE, 4=Opera, 0=others
_bro=(window.opera?4:(document.all?3:(document.getElementById?1:(document.layers?2:0))));
// _ie5: true=IE5+
_ie5=(navigator.appName.indexOf('Microsoft Internet Explorer')>=0
&& document.getElementById)?true:false;
/* このSCRIPT要素内(SCRIPTタグで囲まれている部分)では、下記のスクリプト本体で呼び出される
基本関数の内容を定義します。
スクリプト本体で呼び出される基本関数の定義コードを、基本関数群についてのページからコピーして
この部分に貼り付けるか、Sampleのソースを直接ご覧になって、基本関数が定義されている
SCRIPT要素部分(SCRIPTタグで囲まれている部分)を、そのまま丸ごとコピーしてお使い下さい。*/

/* このサンプルで使用している基本関数

*/


/* Mozilla(NN6/7)だと、文字列を含むタイトなレイヤの場合にリンクの下線が表示されない。
その対策として、スタイルシートで、padding-bottom:1pxとしている。*/

if(_bro==1){ document.write( '<style type="text/css">\n' +'.item{ padding-bottom:1px; }\n' +'<\/style>' ); } //--></script> <script language="JavaScript1.2"><!-- // スクリプト本体部分 blockN = 4; // id名 "b"+数字のレイヤの数(このサンプルの場合は、b0〜b3までの 4個 ) menuWidth=200; /* 折たたみ式メニューの横幅。 スタイルシートで指定した width の値(横幅)と同じ値にすること! */ menu_dy = 5; /* 1アクションごとにスライド移動する距離。  この値を大きくするとスライド移動のスピードが速くなる。*/ interval = 30; // タイマインターバル。 この値を小さくするとスライド移動のスピードが速くなる。 openColor='#8000ff'; // メニューが開いたときの、その開いた項目の文字色 closeColor='#804000'; // メニューが閉じたときの、その閉じた項目の文字色 var sMenu=new Array(); tid=null; active=false; /* メニューの各項目の先頭にある、+ や - の文字の代わりに、メニューの開閉に伴い   画像を変えたい場合には、以下の緑色の部分をソースに加える。   これは応用サンプル含め、全サンプル共通。 */ var pre_img=new Array(); function preLoad(){ for(var i=0; i<2; i++){ pre_img[i] = new Image(); /* + や - の代わりに使う画像には、それぞれ、mark0.gif、mark1.gif の名前を付けておく。 */ pre_img[i].src='mark'+i+'.gif'; } } preLoad(); // コンストラクタ function SlideMenu(div,d,num){ this.div=div; if(document.getElementById) this.itemDiv=getCoDivFromName('b'+num+'_item'); this.d=d; this.opened=false; return this; } SlideMenu.prototype.open = function(i){ // 折りたたみ式メニューを開く関数 var div=this.div; if(!document.layers && document.getElementById){ /* この部分で、折たたみ式メニューを開くために各項目をクリックした時 項目の前の + 部分が、- に変わるようにしています。 なお、このサンプルでは単純なテキストを使っていますが、<span class="togg">+ </span> となっている代わりに、<img src="mark0.gif"> の画像タグを使えば、メニューを開いた時に その画像を変える事もできます。 その場合、ソース中の紫色の部分を this.itemDiv.firstChild.src=pre_img[1].src; に変えれば OK です。 メニューを閉じる時はその逆で this.itemDiv.firstChild.src=pre_img[0].src; です。*/ this.itemDiv.firstChild.firstChild.nodeValue='- '; this.itemDiv.firstChild.nextSibling.style.color=openColor; } this.dx=0; this.dy=this.d; if(getDivTop(div)<(itm_h[i]+cnt_h[i])-this.d && !this.opened){ moveDivBy(div,this.dx,this.dy); return false; } else{ moveDivTo(div,0,itm_h[i]+cnt_h[i]); this.opened=true; active=false; return true; } } SlideMenu.prototype.close = function(i){ // 折りたたみ式メニューを閉じる関数 var div=this.div; if(!document.layers && document.getElementById){ this.itemDiv.firstChild.firstChild.nodeValue='+ '; this.itemDiv.firstChild.nextSibling.style.color=closeColor; } this.dx=0; this.dy=-this.d; if( getDivTop(div)>(itm_h[i]+this.d) && this.opened ){ moveDivBy(div,this.dx,this.dy); return false; } else{ moveDivTo(div,0,itm_h[i]); this.opened=false; active=false; return true; } } function slideMenuToggle(i){ // 折たたみ式メニュー開閉実行関数 if(!sMenu[i]||active) return; else { active=true; slide(i); } } function slide(i){ // スライド運動実行関数 if(!sMenu[i].opened){ if(!sMenu[i].open(i)) tid=setTimeout('slide('+i+')',interval); } else { if(!sMenu[i].close(i)) tid=setTimeout('slide('+i+')',interval); } } var itm_h=new Array(); // id名 "b"+数字+"_item"のレイヤの高さの配列 var cnt_h=new Array(); // id名 "b"+数字+"_content"のレイヤの高さの配列 var blockDiv=new Array(); // id名 "b"+数字のレイヤオブジェクトの配列 var itmDiv=new Array(); // id名 "b"+数字+"_item"のレイヤオブジェクトの配列 var cntDiv=new Array(); // id名 "b"+数字+"_content"のレイヤオブジェクトの配列 subTotal = 0; /* 2階層目のレイヤ群の内容領域の合計値。 すなわち、id名 "b"+数字+"_item"のレイヤの高さと id名 "b"+数字+"_content"のレイヤの高さの合計値。*/ function getCoDivFromName(nm,num,cs){ // レイヤ名からレイヤオブジェクトを得る関数 if(document.layers){ var len, s=''; if(arguments.length>1){ switch(cs){ case 0: len=num; break; case 1: len=num-1; break; } for(var i=0; i<=len; i++) s+='document.layers.'+'b'+i+'.'; } return eval(s+'document.layers.'+nm); } else return document.getElementById? document.getElementById(nm):document.all(nm); } function init(){ for(var i=0; i<blockN-1; i++){ itmDiv[i]=getCoDivFromName('b'+i+'_item',i,0); initDivSize(itmDiv[i]); itm_h[i]=getDivHeight(itmDiv[i]); subTotal+=itm_h[i]; cntDiv[i]=getCoDivFromName('b'+i+'_content',i,0); initDivSize(cntDiv[i]); cnt_h[i]=getDivHeight(cntDiv[i]); subTotal+=cnt_h[i]; moveDivTo(cntDiv[i],0,itm_h[i]); } for(var i=1; i<=blockN-1; i++){ blockDiv[i]=getCoDivFromName('b'+i,i,1); moveDivTo(blockDiv[i],0,itm_h[i-1]); resizeDivTo(blockDiv[i],menuWidth,subTotal-getDivTop(blockDiv[i])); sMenu[i-1]=new SlideMenu(blockDiv[i],menu_dy,i-1); } blockDiv[0]=getCoDivFromName('b0'); resizeDivTo(blockDiv[0],menuWidth,subTotal); setDivVisibility(blockDiv[0],true); } function cancel(){ if(tid){ clearTimeout(tid); tid=null; } } // --></script> </head> <body style="background-color:#ffffff" onLoad="init()" onUnload="cancel()"> <!-- documentの背景色はレイヤの背景色と必ず同じ色にする。 --> <div class="parent" id="b0"> <!-- もっとも外側の親レイヤの id名を"b0"とする --> <div class="item" id="b0_item"><span class="togg">+ </span><a href="#" onClick="slideMenuToggle(0); return false">DHTML/JavaScript</a></div> <div class="content" id="b0_content"> <ol><li><a href="DHTML_samp01.htm">タイプライター文字</a> <li><a href="DHTML_samp02.htm">バウンドオブジェクト</a> <li><a href="DHTML_samp03.htm">画像ワイプ</a> <li><a href="DHTML_samp11.htm">グラデーション文字</a></ol> </div> <div class="child" id="b1"> <div class="item" id="b1_item"><span class="togg">+ </span><a href="#" onClick="slideMenuToggle(1); return false">CSS Browser Check</a></div> <div class="content" id="b1_content"> <ol><li><a href="../../css/style1.htm">フォントとテキスト</a> <li><a href="../../css/style2.htm">色と背景</a> <li><a href="../../css/style3.htm">ボックス</a> <li><a href="../../css/style4.htm">表示方式と配置方法、リスト、ユーザーインターフェイス</a> <li><a href="../../css/explanation.htm">CSS2基礎知識</a></ol> </div> <div class="child" id="b2"> <div class="item" id="b2_item"><span class="togg">+ </span><a href="#" onClick="slideMenuToggle(2); return false">LINKS</a></div> <div class="content" id="b2_content"> <ol> <li><a href="http://www.din.or.jp/~hagi3/JavaScript/JSTips/Default.htm">JavaScript Tips集</a> <li><a href="http://www.htmlperfect.com/index.phtml">Soft Research Lab.</a> <li><a href="http://www.microsoft.com/japan/developer/scripting/">JScript リファレンス</a> <li><a href="http://www.developer.netscape.com/tech/javascript/index.html" >JavaScript リファレンス</a> </ol></div> <div class="child" id="b3"></div> </div></div></div> </body> </html>

Comment

最初は赤色の部分を書き換えてみて下さい。

一番外側の親レイヤの id名を"b0"とし、そこから"b1"以降のレイヤを次々と入れ子にしていきます。
このサンプルでは"b3"までとして、3階建ての折たたみ式メニューとしていますが、このサンプルをベースにして適宜増減させてみてください。

基本的には、応用バージョン含め、HTMLやスタイルシートが分かる程度の人なら設置できるように 作っていますので、すぐに分かると思います。

その他に設置のポイントとしては、BODYタグ内のHTMLで、

<div class="child" id="b1"> <div class="item" id="b1_item"><span class="togg">+ </span><a href="#" onClick="slideMenuToggle(1); return false"> ...

のようになっていますが、slideMenuToggle(1)のように、親レイヤのid名 "b1"の番号1に、slideMenuToggle()()内の番号を合わせることだけです。

応用サンプルなども、赤字の部分だけを変えれば設置できるように作っていますので、基本的にすべて同じ要領で見ていって下さい。

※ なお、「デフォルトではサブメニュー部分が隠れていて、クリックするとサブメニューが 開いたり閉じたりするが、複数レイヤのうちの1つだけを最初から 開いたままに設定することは可能か。」という御質問があったのですが、たとえば、2番目の項目を最初から開いたままにするのは、ソース中の次の部分を以下のように変えます。

for(var i=1; i<=blockN-1; i++){
  blockDiv[i]=getCoDivFromName('b'+i,i,1);   
  moveDivTo(blockDiv[i],0,itm_h[i-1]);
  resizeDivTo(blockDiv[i],menuWidth,itmTotal+cntTotal-getDivTop(blockDiv[i]));
  sMenu[i-1]=new SlideMenu(blockDiv[i],menu_dy);
} 
という部分がソース中にありますが、これを以下のソースのようにします。
for(var i=1; i<=blockN-1; i++){
  blockDiv[i]=getCoDivFromName('b'+i,i,1);
  if(i==2) moveDivTo(blockDiv[i],0,itm_h[i-1]+cnt_h[i-1]);  
  else moveDivTo(blockDiv[i],0,itm_h[i-1]);
  resizeDivTo(blockDiv[i],menuWidth,itmTotal+cntTotal-getDivTop(blockDiv[i]));
  if(i!=2) sMenu[i-1]=new SlideMenu(blockDiv[i],menu_dy);
}  
また、1番目の項目を開いたままにするなら、上の赤字の 2 の部分を 1 に、3番目の項目を開いたままにするなら、3 に変えるだけです。
さらに、1番目と3番目の2つを開いたままにするなら、
if(i==2) となっている部分を if(i==1 || i==3)に、
if(i!=2) となっている部分を if(i!=1 && i!=3)
のようにします。

というわけで、改造したサンプル例はこちらです。


※ 最後に、3階層版、コンパクト版などの応用サンプルに関しては、こちらのリンクからたどって下さい。



Copyright(C) 2000-2005 Yoochan.
All rights reserved.