(jQuery) 最も単純なサブメニュー開閉
(最終更新: 2016-10-14)
- デモ: https://sutara79.github.io/demo-simple-foldable-list/
- ソースコード: https://github.com/sutara79/demo-simple-foldable-list/
解説
下記のリスト要素を開閉式メニューにしたい場合。
<ul id="main_menu">
<li><a href="#">見出し1</a>
<ul>
<li><a href="#">サブメニュー1</a></li>
<li><a href="#">サブメニュー1</a></li>
<li><a href="#">サブメニュー1</a></li>
</ul>
</li>
<li><a href="#">見出し2</a>
<ul>
<li><a href="#">サブメニュー2</a></li>
<li><a href="#">サブメニュー2</a></li>
<li><a href="#">サブメニュー2</a></li>
</ul>
</li>
<li><a href="#">見出し3</a>
<ul>
<li><a href="#">サブメニュー3</a></li>
<li><a href="#">サブメニュー3</a></li>
<li><a href="#">サブメニュー3</a></li>
</ul>
</li>
</ul>
マウスホバーによる開閉。
$('#main_menu > li').hover(
function(){ $(this).children('ul').slideDown() },
function(){ $(this).children('ul').slideUp() }
);
クリックによる開閉。
ただ、このままでは他で開いているサブメニューを閉じることができないので、
$('#main_menu > li').click(function(ev){
var sub = $(this).children('ul');
if ($(sub).is(':hidden')) $(sub).slideDown();
else $(sub).slideUp();
});
下記のようにします。
さらに、メニュー以外の、ページ内の全く関係のない場所をクリックしたら、全てのサブメニューを閉じるようにします。
$('#main_menu > li').click(function(ev){
var sub = $(this).children('ul');
if ($(sub).is(':hidden')) {
$('#main_menu > li > ul:visible').slideUp();
$(sub).slideDown();
} else {
$(sub).slideUp();
}
});
そうなると、"else"以降の記述は余計なので削除します。
//基本的に、ページ内のどこをクリックしても全てのサブメニューを閉じるようにしておく。
$(document).click(function() { $('#main_menu > li > ul').slideUp() });$('#main_menu > li').click(function(ev){
var sub = $(this).children('ul');
if ($(sub).is(':hidden')) {
// 今回はこれからサブメニューを開きたい項目をクリックしているので、
// 上記の全てのサブメニューを閉じるイベントを発火させてはならない。
// よって、イベントのバブリングを中止する。
ev.stopPropagation();$('#main_menu > li > ul:visible').slideUp();
$(sub).slideDown();
} else {
$(sub).slideUp();
}
});
最終形は下記。
$(document).click(function() { $('#main_menu > li > ul').slideUp() });
$('#main_menu > li').click(function(ev){
var sub = $(this).children('ul');
if ($(sub).is(':hidden')) {
ev.stopPropagation();
$('#main_menu > li > ul:visible').slideUp();
$(sub).slideDown();
}
});
余談
ある要素のグループで、イベントが起きた要素以外の要素を取得するにはどうしたらいいのかな、と悩みました。イベントハンドラの内部で、
のような書き方ができれば簡単なのですが。
$('あるグループ:not(this)').css('color', 'red');
残念ながらそういうセレクタの指定はできなので、今回のように
イベントが起きた要素の状態で分岐を作り、その一方の内部で、
イベントが起きた要素には絶対に当てはまらないセレクタを指定することで
"その他"を選り分けることにしました。
他に考えられる方法は、イベントが起きた要素にクラス名を与える方法でしょうか。
これなら、
という書き方ができます。
$('あるグループ').removeClass('selected');
$(this).addClass('selected');
$('あるグループ:not(.selected)').css('color', 'red');
もうひとつ。filter()を使う方法。
うーん…、もっと賢い方法があるのかもしれません。
$('あるグループ').click(function(ev) {
$('あるグループ').filter(function() {
return !$(this).is(ev.target);
}).css('color', 'red');
});
※ 当ブログ内の"サブメニュー"に関する記事