[mootools] 簡単なドロップダウンメニューの作り方

たくさんのメニューを小さな場所へ収めるのにドロップダウンメニューはもってこいの方法で、
色々とスタイリッシュなライブラリーが出てますが、ドロップダウンするのが1箇所だけだったら自作でもいいんじゃないの?
ってことで簡単にmootoolsでのドロップダウンメニューの作り方を書いてみます。

ドロップダウンメニュー 完成サンプル

サンプルページ

まずXHTMLソースは次のように、ボタンになるリンクの下(兄弟)にulとかでリストメニューを作り
識別用のIDをボタンに、クラスを子メニューにつけておきます。

1
2
3
4
5
6
7
8
9
10
11
12
<ul id="navigation">
    <li><a href="#" title="サンプル1">サンプル1</a></li>
    <li><a id="sampleMenu" href="#" title="サンプル2">サンプル2</a>
        <ul class="childMenu">
        <li><a href="#" title="Menu1">Menu1</a></li>
        <li><a href="#" title="Menu2">Menu2</a></li>
        <li><a href="#" title="Menu3">Menu3</a></li>
        <li><a href="#" title="Menu4">Menu4</a></li>
        </ul>
    </li>
    <li><a href="#" title="サンプル3">サンプル3</a></li>
</ul>

説明続く。(結構長いです)

XHTMLを書いたら適当にスタイリングをしましょう。
ドロップダウンメニューになるリストを表示された時の位置に絶対指定で配置して、display:noneで非表示にしておきます。
ブラウザーの表示誤差を吸収するのを忘れずに。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
a {
    text-decoration:none;
}
ul {
    margin:0;
    padding:0;
}
ul#navigation {
    position:relative;
    float:left;
}
ul#navigation li {
    float:left;
    list-style:none;
    width:100px;
    text-align:center;
}
 
ul#navigation a {
    display:block;
    height:30px;
    background-color:#333;
    color:#fff;
    line-height:30px;
    text-align:center;
}
ul#navigation a:hover {
    background-color:#666;
}
ul#navigation ul.childMenu {
    position:absolute;
    width:100px;
    top:30px;
    left:100px;
}
ul#navigation ul.childMenu li {
    float:none;
}
 
ul#navigation ul.childMenu a {
    display:block;
    background-color:#128E2D;
    color:#fff;
}
ul#navigation ul.childMenu a:hover {
    background-color:#66CC33;
}
.childMenu {
    display:none;
}

FirefoxとIEはz-indexの解釈が違うので、z-indexの指定が非常に重要です。
どのように違うのかは参考のリンク先を見てみてください。
参考» 調べものブログ:IEとFirefoxでz-indexの扱いが異なる

javascript
改変出来るように順を追って説明します。

まずはボタンになるa要素とドロップダウンさせるリスト要素を取得します。
サンプルのソースではリストはボタンの次にある要素なので、getNext()で得られます。

1
2
3
4
window.addEvent('domready', function(){
    var parentMenu = $("sampleMenu");
    var childMenu = parentMenu.getNext(".childMenu");
});

ドロップダウンメニューの動きを大きく分けると3つになります。

  • ボタンにマウスが乗ったらサブメニューを表示
  • ボタンとサブメニューからマウスが離れたらサブメニューを隠す
  • サブメニューにマウス乗ってる間はずっと表示

「乗ってる間」はoverと同じなので、使うイベントハンドラは「乗ったとき」の”mouseover”と「離れた時」の”mouseleave”になります。
“mouseout”じゃない理由はソース書き変えて実際に動作を見てみてください。

要素へのイベントの追加はaddEventメソッドで行えますが、今回は2つ同時に追加しなくてはならないのでaddEvents()を使います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
parentMenu.addEvents({
    "mouseover":function(){
        childMenu.setStyle("display","block");
    },
    "mouseleave":function(){
        childMenu.setStyle("display","none");
    }
});
childMenu.addEvents({
    "mouseover":function(){
        this.setStyle("display","block");
    },
    "mouseleave":function(){
        this.setStyle("display","none");
    }
});

マウスオーバー時にスタイルを変更して隠してたリストを表示。マウスが離れたら逆に隠します。
childMenuの方のイベントは自身が対象なので、関数内では”this”を使います。

ここまで書けばドロップダウンっぽい動きはしますが、気になる点が幾つか…

  1. タイトルがいちいち表示されてうざい
  2. 消えるのが早すぎる

とりあえず上記2点を簡単に解決しときます。

まずタイトルですが、これは属性なのでremoveProperty()を使用します。

1
2
3
4
5
6
7
var parentMenu = $("sampleMenu");
var childMenu = parentMenu.getNext(".childMenu");
//タイトル消す
parentMenu.removeProperty("title");
childMenu.getChildren().each(function(li){
    li.getElement("a").removeProperty("title");          
});

ドロップダウンさせるメニューの方はli要素を取得してからeach()で1つずつ処理します。

次に、遅延用の変数を追加。場所はどこでもいいです。

1
var timer =null;

この変数はdelayによる遅延をキャンセルする際に使います。

上で書いたドロップダウンメニューのaddEvents()を変更します。
display:noneを関数にしてdelayを適用し、返り値が変数timerに入るようにします。
わざわざ関数にするのはdelayが関数にしか使えないからです。

1
2
3
4
5
6
7
8
9
10
childMenu.addEvents({
    "mouseover":function(){
        this.setStyles({"display":"block"});
        if(timer){$clear(timer);}
    },
    "mouseleave":function(){
        fnc = function(){this.setStyle("display","none");}
        timer = fnc.delay(1000,this);
    }
});

mouseoverには「timerに何か入ってたら$clear()する」というソースを追加しました。
$clear()はdilay()などのタイマーを止める関数です。

同じようにボタンのイベントにも$clear()を追加します。

1
2
3
4
5
6
7
8
9
parentMenu.addEvents({
    "mouseover":function(){
        childMenu.setStyle("display","block");
        if(timer){$clear(timer);}
    },
    "mouseleave":function(){
        childMenu.setStyle("display","none");
    }
});

これでちょっとドロップダウンメニューらしさが増しました!(゚∀゚)
この程度ならフレームワーク使わなくても簡単に書けるレベルですが、
Fx.Tweenでスライドさせたり、fade()でフェードイン・アウトさせたりとちょっとしたエフェクトをつけても面白いです。(その分ソースはややこしくなりますが)
タブを使った場合にも対応させるなら’focus’と’blur’、キー動作に対応させるなら’keydown’を追加します。

「[mootools] 簡単なドロップダウンメニューの作り方」への6件のフィードバック

  1. こんにちは!最近サイトを作ってみました。
    メニューがたくさんあるのでドロップダウンメニューにしたいなぁと思っています。
    詳しく説明してあってとってもわかりやすくってありがたいです☆

    私のサイトですが、メニューが縦に積み重なってるんです。
    しかも、ドロップボックス(?)はなく、単純にテキストを書いてある所にリンクを張っただけなんですが、
    それでも、ドロップダウンメニューをドロップして右、そのあと下にだすことって可能なんでしょうか???

    こんなイメージです。



    1にマウスをおくと、1からa.b.cが出てくる、という感じにしたいです。

    1——a
    2 -b
    3 -c

    これは縦に積み重なっているので複雑なんでしょうか??

    返信
  2. waka さん >

    この記事で作るドロップダウンメニューは、記事冒頭にある画像のように
    CSSでメニューを作って完成状態にしてから
    JavaScriptでdisplayプロパティを変更して表示非表示を切り替える。というものです。
    なのでCSSで希望する位置にメニューを配置すれば縦型も作れます。

    記事にあるサンプルソースだと30行目にあるセレクターで子メニューの位置を指定しています。
    position:absolute で絶対位置指定にし、topとleftで.childMenuの表示位置を決定するものです。
    top → メニューの高さと同じ=下に出る  left → 親ボタンの位置と同じ

    このとき、childMenu は左上の基準点を position:relative が指定してある ul#navigation と同じにするのですが、
    縦並びだと各ボタンによって縦の位置が変わるので、ul#navigation の position:relative を ul#navigation li へ移動し
    top:0; left:100px(メニューの幅と同じ) とすれば縦型に配置出来ると思います。
    なお、floatはすべて削除してください。

    返信
  3. こんにちは!
    wakaです。
    詳しくおしえていただいて、ありがとうございます!!!

    さっそくやってみます!(できるかなぁ・・・・。)
    がんばりまーーーーす!

    返信
  4. waka@
    こんにちは。
    さっそくやってみました。
    floatの部分は削除しましたが、
    float:none;こちらも削除しました。

    topもleftも教えてもらった通りに指定したんですが、
    もしかして、表示するための箱のwidthが100pxでは大きすぎて表示されないのかなぁと思い、
    10pxにしてみましたが、ピクリともしません(涙)

    ul#navigation ul.childMenu {
    position:absolute;
    width:100px;
    top:0px;
    left:100px;
    もうちょっといじってみます。

    返信
  5. わか@

    何度もすみません。
    >縦並びだと各ボタンによって縦の位置が変わるので、ul#navigation の position:relative を ul#navigation li へ移動し

    この移動っていうのはどういうことですか??
    初歩的なことでお恥ずかしいですが、おしえてください。
    おねがいします。

    返信

コメントを残す

This site uses Akismet to reduce spam. Learn how your comment data is processed.