0

I am creating a dropdown menu, and have an issue with opening and closing it.

When you click on link (.appHeader a.user) it opens a hidden ul (.appHeader ul.user-menu). Until now everything is working as expected. But when i re-click the same link (with the ul open), it closes it and re-opens it.

Because i have an animation on the ul show (fadeIn) it creates a very nasty animation loop if you keep clicking it several times fast.

HTML:

<div class="appHeader">
    <div>
        <a class="user" href="#">Welcome, <span>Name Lastname</span></a>
        <ul class="user-menu">
            <div></div>
            <li><a href="#">Menu item</a></li>
            <li><a href="#">Menu item</a></li>
            <li><a href="#">Menu item</a></li>
            <li><a href="#">Menu item</a></li>
        </ul>
    </div>
</div>

CSS:

.appHeader ul.user-menu {
    display: none;
    position:absolute;
    z-index: 900;
    width: 150px;
    border: 1px solid #111111;
    background: #FFFFFF;
    box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.2);
    text-align: left;
    list-style: none;
    padding: 10px 0;
    -webkit-border-radius: 4px; 
    -moz-border-radius: 4px; 
    -ms-border-radius: 4px; 
    -o-border-radius: 4px; 
    border-radius: 4px; 
} 
.appHeader ul.user-menu li {
    margin:0;
    padding:0 10px;
    display: block;
}
.appHeader ul.user-menu li a {
    height: 15px;
    text-transform: none;
    background: #FFFFFF;
    line-height: 1em;
    font-size: 1em;
    display: block;
    text-decoration: none;
    color: #222222;
    padding:0;
    padding: 8px 10px;
    outline: none;
}
.appHeader ul.user-menu li a:hover { 
    background: #f2f2f2; 
    -webkit-border-radius: 3px;
    -moz-border-radius: 3px;
    -ms-border-radius: 3px;
    -o-border-radius: 3px;
    border-radius: 3px;
}
.appHeader ul.user-menu li a:active { 
    background: #CCCCCC; 
    color: #111111;
}
.appHeader ul.user-menu div {
    height: 8px;
    width: 16px;
    background: #FFF;
    margin:-18px 0 10px 120px;
    background: url(data:image/png;base64,...) no-repeat;
}

JS:

$('.appHeader a.user').on('click', function() {
    $('.appHeader ul.user-menu').fadeIn('fast');
    $(document).mouseup(function (e) {
        var container = $('.appHeader ul.user-menu');
        if (!container.is(e.target) && container.has(e.target).length === 0) {
            $('.appHeader ul.user-menu').fadeOut('fast');
        }
    });
});

Here is the JSFiddle for the problem http://jsfiddle.net/MxfPq/4/ (try clicking the name fast several times)

Is there any way i can disable the click while the ul is shown? and then re-enabling the click when the ul is hidden ?

I have tried with the .one() jQuery function, but this is not the functionality i want, this disables the second click entirely.

  • Why don'y you use some kind of flag? – Kamil T Oct 11 '13 at 15:31
  • http://stackoverflow.com/questions/970388/jquery-disable-a-link – Sterling Archer Oct 11 '13 at 15:32
  • How about only calling `.fadeIn()` if the menu is not showing. One approach that might work is adding [`:hidden`](http://api.jquery.com/hidden-selector/) to your selector. You also need to move the `$(document).mouseup` outside the `.on('click')` otherwise you are adding a new mouseup event handler every time the link is clicked. – andyb Oct 11 '13 at 15:34
  • What I usually do is add a class (visible) to the element, also for CSS purposes: http://jsfiddle.net/MxfPq/10/ – Joshua - Pendo Oct 11 '13 at 15:35
  • What do you want it to work like? Why a "mouseup" listener? Click link once it appears? Click link again it disappears? Click option, it disappears? But you said you wanted click disabled when menu was visible so it wouldn't disappear in that case? – Lee Meador Oct 11 '13 at 15:54
  • @RUJordan sorry, i have missed that post apparently, but i got some great responses as alternatives, which really simply my code. –  Oct 11 '13 at 16:31

4 Answers4

1

You don't need to disable the click. Simply check if the element is visible before calling the fadeIn function:

if(!$('.appHeader ul.user-menu').is(':visible'))
{
    $('.appHeader ul.user-menu').fadeIn('fast');
}   

Full jQuery:

$('.appHeader a.user').on('click', function() {
    if(!$('.appHeader ul.user-menu').is(':visible'))
    {
        $('.appHeader ul.user-menu').fadeIn('fast');
    }    
    $(document).mouseup(function (e) {
        var container = $('.appHeader ul.user-menu');
        if (!container.is(e.target) && container.has(e.target).length === 0) {
            $('.appHeader ul.user-menu').fadeOut('fast');
        }
    });
});

DEMO: http://jsfiddle.net/MxfPq/8/

Axel
  • 10,404
  • 2
  • 27
  • 43
  • Thank you so much, eventually i found an alternative answer, but your version is much more clean, so i ended up using it. –  Oct 11 '13 at 16:32
0

if you really want to prevent the click. try preventdefault. otherwise, Axel's answer is the best way imo. I've added the preventdefault usage to Axel's answer.

$('.appHeader a.user').on('click', function(event) {
    if(!$('.appHeader ul.user-menu').is(':visible'))
    {
        $('.appHeader ul.user-menu').fadeIn('fast');
    }
    else
    {
        event.preventDefault();
    } 
    $(document).mouseup(function (e) {
        var container = $('.appHeader ul.user-menu');
        if (!container.is(e.target) && container.has(e.target).length === 0) {
            $('.appHeader ul.user-menu').fadeOut('fast');
        }
    });
});
Bcbury
  • 119
  • 1
  • 9
0

There is a problem in that you create another mouseup listener every time the user clicks. This will pile up the listeners and eventually cause an issue after too many clicks.

You need seperate listeners for the two events. Something like:

$('.appHeader a.user').on('click', function() {
    $('.appHeader ul.user-menu').fadeIn('fast');
});

$(document).mouseup(function (e) {
    var container = $('.appHeader ul.user-menu');
    if (!container.is(e.target) && container.has(e.target).length === 0) {
        $('.appHeader ul.user-menu').fadeOut('fast');
    }
});

Its strange you only fade it out when the mouse-up is over the same area. If the user moves the mouse away, it won't happen.

But a click is a down plus an up so how would there be a mouseup after a click unless there was a 2nd click? I think I don't understand how you want it to work. See my note above.

Lee Meador
  • 12,221
  • 1
  • 29
  • 38
  • I found out this the hard way and eventually found an working version, but ended up going with @Axel 's suggestion –  Oct 11 '13 at 16:34
0

Check that its not already open:

$('.appHeader a.user').on('click', function() {
    if($('.appHeader ul.user-menu').is(':hidden')){
        $('.appHeader ul.user-menu').slideDown('fast');
    } else {
        $('.appHeader ul.user-menu').slideUp('fast');
    }
    return false;
});

http://jsfiddle.net/MxfPq/12/

Moob
  • 13,593
  • 1
  • 29
  • 45
  • This is what i ended up going with, by @Axel's suggestion. Thank you. –  Oct 11 '13 at 16:35