折たたみ式メニュー

こちらは、折りたたみ式メニューの3階層版解説ページです。

はじめての方は、まず、2階層版の解説ページ からご覧ください。


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

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

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


Source Code

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

/* 各レイヤの背景色を、必ずページの document の背景色(この場合、#ffffff)に合わせる。*/
.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; cursor:hand; cursor:pointer; } .content{ position:absolute; width:200px; background-color:#ffffff; layer-background-color:#ffffff } .togg{ color:#ff0000 } /* +や-などの文字のデフォルトでの色 */ .togtag{ color:#804000; text-decoration:underline } /* 各項目のデフォルトの文字色 */ --></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 span{ padding-bottom:1px; }\n' +'<\/style>' ); } //--></script> <script language="JavaScript1.2"><!-- // スクリプト本体部分 blockN = 4; // id名 "b"+数字のレイヤの数(このサンプルの場合は、b0〜b3までの 4個 ) /* * 3階層目のサブブロックを持つ2階層目のブロックを、下記の配列で、以下のように指定します。 * * double_block=[[3階層目のサブブロックを持つ id名 "b"+数字のレイヤのその数字, * そこに含まれるサブブロックの数, * [内容のレイヤ(id名に'_content'が含まれるレイヤ)を含まない3階層目の * ブロックがある場合は、そのブロック(レイヤ)の id名 "b"+数字+数字 のその最後の数字。]]]; * * このサンプルを例にとって解説します。 * * まず、このサンプルでは、3階層の入れ子になったサブブロックを持つ id名 "b"+数字 * の親レイヤは、"b0"です。したがって、最初の数字は 0 となります。 * 次に、"b0"のレイヤには、id名 "b00"〜"b03"の4個のレイヤが、サブのブロックとして * 入れ子になっています。したがって、そのサブブロックの個数である 4個の 4 が次の数字になります。 * 次に、id名 "b03"のレイヤには、内容のレイヤとなる、id名 "b03_content"のレイヤは * 含まれていません。その場合は、最後の[]に入れる数字として、その含まれていない * "b03" の最後の数字である 3 を指定し、[3]とします。 * また、もし、id名 "b00"のレイヤと、id名 "b03"のレイヤの2つが内容のレイヤを含まないなら * 最後の[]部分は、[0,3]というようにします。 * "b00"〜"b03"の 4つとも内容のレイヤを含まないなら [0,1,2,3] です。 * なお、すべてのレイヤが内容のレイヤを含むことはありません。 * その場合は必ず、最後の番号のレイヤが空のレイヤとなる必要があり、 * その空のレイヤは内容を含まないからです。 * * 以上のようにして設定すると、double_block=[[0,4,[3]]]; というようになります。 * * 次に、このサンプルでは、3階層の入れ子になったサブブロックを持つ id名 "b"+数字の親レイヤが、 * "b0"の他に、もう一つ、"b2" もあります。 * そして、その "b2" に含まれるサブブロックの個数は、2個です。 * また、内容のレイヤとなる id名 "b2"+数字+_content のレイヤは、id名 "b21" のレイヤには * 含まれていません。 * 以上から、このサンプルの場合は次のように設定されます。 * * double_block=[[0,3,[2]],[2,2,[1]]]; * * この要領で、3階層の入れ子になったサブブロックを持つ id名 "b"+数字のレイヤがある限り、同様に追加して * していって下さい。 * double_block=[[0,3,[2]],[2,2,[1]], .....]; * */ double_block=[[0,4,[3]],[2,2,[1]]]; menuWidth=200; /* 折たたみ式メニューの横幅。 スタイルシートで指定した width の値(横幅)と同じ値にする。 */ menu_dy = 5; /* 1アクションごとにスライド移動する距離。 この値を大きくするとスライド移動のスピードが速くなる。*/ interval = 30; // タイマインターバル。 この値を小さくするとスライド移動のスピードが速くなる。 openColor='#8000ff'; // メニューが開いたときの、その開いた項目の文字色 closeColor='#804000'; // メニューが閉じたときの、その閉じた項目の文字色 margin_bottom=7; // 3階層目の最後の項目が折りたたまれている時          // その直下の2階層目の項目とのマージンを設定します(単位:px) var sMenu=new Array(); tid=null; active=false; // コンストラクタ /* SlideMenu(1アクションごとにレイヤがスライド移動する距離(単位: px), 入れ子になっているサブメニューを持っているか?(true: サブメニューあり、false: 無し), レイヤの中に、項目のレイヤ(id名に'_item'が含まれるレイヤ)と 内容のレイヤ(id名に'_content'が含まれるレイヤ)の両方のレイヤを含むか? (true: 両方ともある、 false: 片方(項目のレイヤ)しかない。), 以下(i,j...)- レイヤを識別するための番号。) */ function SlideMenu(d,hasChild,hasCont,i,j){ if(arguments.length==4){ // 2階層目 this.moveDiv=getDivFromName('b'+(i+1)); // 実際に動くレイヤ this.itemDiv=getDivFromName('b'+i+'_item'); // 項目のレイヤ this.def=getDivHeight(this.itemDiv); // 初期位置 if(!hasChild){ this.target=this.def+getDivHeight(getDivFromName('b'+i+'_content')); // 目的地 } else this.target = this.def; } else if(arguments.length==5){ // 3階層目  this.moveDiv=getDivFromName('b'+String(i)+String(j+1)); this.sibDiv=new Array(); // 同時に動くレイヤ this.sibDiv[this.sibDiv.length]=getDivFromName('b'+(i+1)); var item_obj=getDivFromName('b'+String(i)+String(j)+'_item'); if(item_obj) this.itemDiv=item_obj; else this.itemDiv=getDivFromName('b'+String(i)+String(j)); this.target=this.def=getDivHeight(this.itemDiv); if(!hasChild && hasCont){ this.target=this.def +getDivHeight(getDivFromName('b'+String(i)+String(j)+'_content')); } } this.hasChild=hasChild; this.d=d; this.count=0; this.child = new Array(); return this; } // サブのメニューオブジェクトを加える関数。 SlideMenu.prototype.addChild = function(obj){ this.child[this.child.length]=obj; this.hasChild=true; if(this.hasChild){ this.target+=obj.def; } } // 折りたたみ式メニューを開く関数 SlideMenu.prototype.open = function(){ var div=this.moveDiv; this.dx=0; this.dy=this.d; if(this.count==0){ var fc=this.itemDiv.firstChild; // + や - の文字の代わりに画像を使うときは // その場合、下の紫色の部分を // this.firstChild.src='画像のURL'; // というように変える(詳しくは2階層版を参照してください。)。 if(fc && fc.className=="togg"&& fc.firstChild) fc.firstChild.nodeValue='- '; if(fc && fc.nextSibling && fc.nextSibling.className=="togtag") this.itemDiv.firstChild.nextSibling.style.color=openColor; if(this.sibDiv){ for(i=0; i<this.sibDiv.length; i++){ this.sibDiv[i].def=getDivTop(this.sibDiv[i]); this.sibDiv[i].target=this.sibDiv[i].def+this.target-this.def; } } } if(getDivTop(div)<this.target-this.d && !this.opened){ moveDivBy(div,this.dx,this.dy); if(this.sibDiv){ for(i=0; i<this.sibDiv.length; i++) moveDivBy(this.sibDiv[i],this.dx,this.dy); } this.count++; return false; } else{ moveDivTo(div,0,this.target); if(this.sibDiv){ for(i=0; i<this.sibDiv.length; i++) moveDivTo(this.sibDiv[i],0,this.sibDiv[i].target); } this.count=0; this.opened=true; active=false; return true; } } // 折りたたみ式メニューを閉じる関数 SlideMenu.prototype.close = function(){ var div=this.moveDiv; this.dx=0; this.dy=-this.d; if(this.count==0){ var fc=this.itemDiv.firstChild; if(fc && fc.className=="togg"&& fc.firstChild) fc.firstChild.nodeValue='+ '; if(fc && fc.nextSibling && fc.nextSibling.className=="togtag") this.itemDiv.firstChild.nextSibling.style.color=closeColor; } if( getDivTop(div)>(this.def+this.d) && this.opened ){ moveDivBy(div,this.dx,this.dy); if(this.sibDiv){ for(i=0; i<this.sibDiv.length; i++) moveDivBy(this.sibDiv[i],this.dx,this.dy); } this.count++; return false; } else{ if(getDivTop(div)>this.def) { this.ddy=-(getDivTop(div)-this.def); } moveDivTo(div,0,this.def); if(this.sibDiv){ for(i=0; i<this.sibDiv.length; i++){ moveDivBy(this.sibDiv[i],this.dx,this.ddy); } } var num = 0; while(num<this.child.length-1){ if(this.child[num]) this.child[num].reset(); num++; } this.count = 0; this.opened=false; active=false; return true; } } // メニューが閉じられた時、初期状態に戻す関数。 SlideMenu.prototype.reset = function(){ moveDivTo(this.moveDiv,0,this.def); var fc=this.itemDiv.firstChild; if(fc && fc.className=="togg" && fc.firstChild ) fc.firstChild.nodeValue='+ '; if(fc && fc.nextSibling && fc.nextSibling.className=="togtag") this.itemDiv.firstChild.nextSibling.style.color=closeColor; this.opened=false; } // 折りたたみ式メニュー開閉実行関数 function slideMenuToggle(){ if(active) return; if((arguments.length==1 && !sMenu[arguments[0]])) return; else if((arguments.length==2 && !sMenu[arguments[0]][arguments[1]])) return; active=true; var fn=''; for(i=0; i<arguments.length; i++){ if(i==0) fn+='slide('+String(arguments[i]); else if(i<arguments.length) fn+=','+String(arguments[i]); if(i==arguments.length-1) fn+=')'; } eval(fn); } function slide(){ // スライド運動実行関数 var sobj=null; if(arguments.length==1){ sobj=sMenu[arguments[0]]; if(!sobj.opened){ if(!sobj.open()) tid=setTimeout('slide('+arguments[0]+')',interval); } else { if(!sobj.close()) tid=setTimeout('slide('+arguments[0]+')',interval); } } else if(arguments.length==2){ sobj=sMenu[arguments[0]][arguments[1]]; if(!sobj.opened){ if(!sobj.open()) tid=setTimeout('slide('+arguments[0]+','+arguments[1]+')',interval); } else { if(!sobj.close()) tid=setTimeout('slide('+arguments[0]+','+arguments[1]+')',interval); } } } var blockDiv=new Array(); // id名 "b"+数字のレイヤオブジェクトの配列 var subBlockDiv=new Array(); // id名 "b"+数字+数字のレイヤオブジェクトの配列 var itmDiv=new Array(); // id名 "b"+数字+"_item"のレイヤオブジェクトの配列 var cntDiv=new Array(); // id名 "b"+数字+"_content"のレイヤオブジェクトの配列 var itm_h=new Array(); // id名 "b"+数字+"_item"のレイヤの高さの配列 var cnt_h=new Array(); // id名 "b"+数字+"_content"のレイヤの高さの配列 subTotal = 0; /* 2階層目のレイヤ群の内容領域の合計値。 すなわち、id名 "b"+数字+"_item"のレイヤの高さと id名 "b"+数字+"_content"のレイヤの高さの合計値。*/ subsubTotal = 0; /* 3階層目のレイヤ群の内容領域の合計値。 すなわち、id名 "b"+数字+数字+"_item"のレイヤの高さと id名 "b"+数字+数字+"_content"のレイヤの高さの合計値。*/ function init(){ for(i=0; i<double_block.length; i++){ subBlockDiv[i]=new Array(); var subBlockTotal=0; var sub_len=double_block[i][1]; for(j=0; j<sub_len; j++){ var sb=subBlockDiv[i][j]=getDivFromName('b'+String(double_block[i][0])+String(j)); sb.childTotal = 0; var item_obj=getDivFromName('b'+String(double_block[i][0])+String(j)+'_item'); if(item_obj) sb.itemDiv=item_obj; else sb.hideDiv=getDivFromName('b'+String(double_block[i][0])+String(j)); if(item_obj){ initDivSize(sb.itemDiv); sb.itemH=getDivHeight(sb.itemDiv); } if(j>0) moveDivTo(sb,0,subBlockDiv[i][(j-1)].itemH); var no_content=false; for(k=0; k<double_block[i][2].length; k++){ if(j==double_block[i][2][k]){ if(sb.itemH) sb.childTotal+=sb.itemH; no_content=true; break; } } if(!no_content){ var cnt_obj=getDivFromName('b'+String(double_block[i][0])+String(j)+'_content'); if(cnt_obj){ sb.cntDiv=cnt_obj; initDivSize(sb.cntDiv); sb.contH=getDivHeight(sb.cntDiv); moveDivTo(sb.cntDiv,0,sb.itemH); sb.childTotal+=sb.contH } sb.childTotal+=sb.itemH; } if(sb.hideDiv&&j>0){ sb.hideDiv.style.fontSize='1px'; resizeDivTo(sb.hideDiv,menuWidth,margin_bottom); sb.childTotal+=margin_bottom; } subBlockTotal+=sb.childTotal; } for(j=0; j<sub_len; j++){ if(!subBlockDiv[i][j].hideDiv){ var height=subBlockTotal-getDivTop(subBlockDiv[i][j]); resizeDivTo(subBlockDiv[i][j],menuWidth,height); } } resizeDivTo(subBlockDiv[i][0],menuWidth,subBlockTotal); subsubTotal+=subBlockDiv[i][0].childTotal; } for(var i=0; i<blockN-1; i++){ itmDiv[i]=getDivFromName('b'+i+'_item'); initDivSize(itmDiv[i]); itm_h[i]=getDivHeight(itmDiv[i]); subTotal+=itm_h[i]; var hasSubBlock=false; for(j=0; j<double_block.length; j++){ if(i==double_block[j][0]) { cntDiv[i]=getDivFromName('b'+String(i)+'0'); hasSubBlock=true; break; } } if(!hasSubBlock){ cntDiv[i]=getDivFromName('b'+i+'_content'); initDivSize(cntDiv[i]); cnt_h[i]=getDivHeight(cntDiv[i]); subTotal+=cnt_h[i]; } moveDivTo(cntDiv[i],0,itm_h[i]); } blockDiv[0]=getDivFromName('b0'); resizeDivTo(blockDiv[0],menuWidth,subTotal+subsubTotal); for(var i=1; i<blockN; i++){ blockDiv[i]=getDivFromName('b'+i); moveDivTo(blockDiv[i],0,itm_h[i-1]); resizeDivTo(blockDiv[i],menuWidth,subTotal+subsubTotal-getDivTop(blockDiv[i])); } for(var i=0; i<blockN-1; i++){ var hasSub=false; for(j=0; j<double_block.length; j++){ if(i==double_block[j][0]){ sMenu[i]=new SlideMenu(menu_dy,true,true,i); hasSub=true; for(k=0; k<double_block[j][1]; k++){ var hasCont=true; for(m=0; m<double_block[j][2].length; m++){ if(k==double_block[j][2][m]){ hasCont=false; break; } } sMenu[i].addChild(sMenu[i][k]=new SlideMenu(menu_dy,false,hasCont,i,k)); } } } if(!hasSub){ sMenu[i]=new SlideMenu(menu_dy,false,true,i); } } 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"> <div class="item" id="b0_item" onClick="slideMenuToggle(0); return false"> <span class="togg">+ </span><span class="togtag">DHTML/JavaScript</span> </div> <div class="child" id="b00"> <div class="item" style="margin-left:10px" id="b00_item" onClick="slideMenuToggle(0,0);return false;"> <span class="togg">+ </span><span class="togtag">タイプライター文字</span> </div> <div class="child" id="b00_content"> <ul> <li><a href="DHTML_samp01.htm">サンプルNo.1</a> <li><a href="DHTML_samp01a.htm">サンプルNo.2</a> </ul> </div> <div class="child" id="b01"> <div class="item" style="margin-left:10px" id="b01_item" onClick="slideMenuToggle(0,1);return false;"> <span class="togg">+ </span><span class="togtag">マウス追跡</span> </div> <div class="child" id="b01_content"> <ul> <li><a href="DHTML_samp18.htm">サンプルNo.1</a> <li><a href="DHTML_samp18a.htm">サンプルNo.2</a> <li><a href="DHTML_samp18b.htm">サンプルNo.3</a> </ul> </div> <div class="child" id="b02"> <div class="item" style="margin-left:10px" id="b02_item" onClick="slideMenuToggle(0,2);return false;"> <span class="togg">+ </span><span class="togtag">スライドオブジェクト</span> </div> <div class="child" id="b02_content"> <ul> <li><a href="DHTML_samp12.htm">サンプルNo.1</a> <li><a href="DHTML_samp12a.htm">サンプルNo.2</a> <li><a href="DHTML_samp17.htm">サンプルNo.3</a> </ul> </div> <div class="child" id="b03"></div> </div></div></div> <div class="child" id="b1"> <div class="item" id="b1_item" onClick="slideMenuToggle(1); return false"> <span class="togg">+ </span><span class="togtag">CSS Browser Check</span> </div> <div class="content" id="b1_content"> <ul> <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> </ul> </div> <div class="child" id="b2"> <div class="item" id="b2_item" onClick="slideMenuToggle(2); return false"> <span class="togg">+ </span><span class="togtag">LINKS</span> </div> <div class="child" id="b20"> <div class="item" style="margin-left:10px" id="b20_item" onClick="slideMenuToggle(2,0);return false;"> <span class="togg">+ </span><span class="togtag">OTHERS</span> </div> <div class="child" id="b20_content"> <ul> <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://developer.netscape.com/tech/javascript/index.html" >JavaScript リファレンス</a> </ul> </div> <div class="child" id="b21"> <div class="item" id="b21_item"> <ul> <li><a href="../../index.htm">HOME</a> <li><a href="../DHTML13.htm">解説</a> </ul> </div></div></div> <div class="child" id="b3"></div> </div></div></div> </body> </html>

Comment

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

詳しい設置法については、ソース中に書きましたが、基本的には一部を除き、2階層版とほとんど同じですので、2階層版の説明と合わせて参照して下さい。

+ や - を画像に代える場合も、2階層版と同様、ソース中の紫色の部分を変更して下さい。
(その際は、2階層版のソース中にある、緑色のソース部分(画像をプリロードするための関数部分)を加えるのを忘れないで下さい。)

その他に、3階層版特有の設置のポイントとしては、BODYタグ内のHTMLで、

<div class="child" id="b00"><div class="item" style="margin-left:10px" id="b00_item" onClick="slideMenuToggle(0,0);return false;"><span class="togg">+ </span> ...

のようになっていますが、slideMenuToggle(0,0)のように、親レイヤのid名 "b00"の番号00に、slideMenuToggle()()内の数字(引数。数字の間にカンマを入れること!)を合わせることです。



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