Reputation: 11602
I'm trying to create a multi-layered menu that has cool sliding / fading-in effects. Right now, it works on my first click, but after that it fails.
HTML
<ul id="menu">
<li>
Development
<div>
<ul>
<li>Sub 1</li>
<li>Sub 2</li>
</ul>
</div>
</li>
<li>
Story
<div>
<ul>
<li>Sub 1</li>
<li>Sub 2</li>
</ul>
</div>
</li>
<li>
News
<div>
<ul>
<li>Sub 1</li>
<li>Sub 2</li>
</ul>
</div>
</li>
</ul>
JavaScript
var observeClicks = function(){
var lis = $('#menu').children('li');
lis.click(function(){
lis.unbind();
var $this = $(this);
var $div = $($this.children('div')[0]);
var $ul = $($div.children('ul')[0]);
var clone = $ul.clone(true, true)
.css({visibility: 'hidden', display: 'block'});
clone.attr('style', clone.attr('style').replace('block', 'block !important'))
.insertAfter($ul);
setTimeout(function(){
$ul.css({height: clone.css('height')});
$div.css({height: clone.css('height')});
clone.remove();
}, 0);
if(!lis.hasClass('current')){
$this.addClass('current');
$div.slideDown(350, function(){
$ul.fadeIn(350);
observeClicks();
});
}else{
if(!$this.hasClass('current')){
$current = $($('.current')[0]);
$cdiv = $($current.children('div')[0]);
$cul = $($cdiv.children('ul')[0]);
$cdiv.css({height: $cul.css('height')});
$this.addClass('current');
$current.removeClass('current');
$cul.fadeOut(350, function(){
$cdiv.slideUp(350, function(){
$div.slideDown(350, function(){
$ul.fadeIn(350);
observeClicks();
});
});
});
}else{
observeClicks();
}
}
});
}
observeClicks();
CSS
#menu {
cursor: pointer;
width: 150px;
}
#menu div {
display: none;
width: 100%;
}
#menu li ul {
display: none;
margin-left: 25px;
}
The first interesting problem that I ran into was, instead of sliding, my menu would just "pop" in. That was because the div didn't take up space until the elements within it were showing. So I overcame that by giving it a fixed area. (Width is set in CSS, and Height is set in JavaScript). I think this is where the problem may lie, I insert a dummy element, read it's height, assign that to the div, and then remove the element. It's sketchy I know but I couldn't find a better way.
Anyways, when I first click on a menu item, it works perfectly. But as soon as I try my second click, it seems that the height does not get set for the div, and there is no sliding effect. Check it out here.
You may also notice, that instead of me trying to mess around with .stop()
for the animations, I just unbind the click handler and then re-bind them after all of the animations have finished. That was deliberate, and I had this problem before adding that in. If someone could show me what to fix that would be much appreciated.
Upvotes: 0
Views: 438
Reputation: 597
It seams that the problem is in the clone-height piece of code. I'm not sure about why it is only working with the first element clicked, but you could fix it by simply setting this elements heights in css:
#menu div {
display: none;
width: 100%;
height: 40px;
}
#menu li ul {
display: none;
margin-left: 25px;
height: 40px;
}
And removing the js code that clone and asign heights, It looks ok in jsfiddle: http://jsfiddle.net/j7EHu/
Upvotes: 1
Reputation: 24276
Please use this instead of your code:
#menu {
cursor: pointer;
width: 150px;
}
#menu div {
display: none;
width: 100%;
}
#menu li ul {
margin-left: 25px;
}
JS
$("#menu > li").click(function(){
$('#menu .active').slideUp(350).removeClass('active');
$(this).find('div').slideDown(350,function() {
$(this).addClass('active');
});
});
Here si a demo: http://jsfiddle.net/yL944/12/
Upvotes: 0