简体   繁体   中英

How do I hide this drop down menu using javascript/jquery when any area outside of it is clicked?

Right now my I click #headerNav and menu drops down as expected but when I click any where outside of the menu it has no effect.

Where am I going wrong?

My HTML:

   <header>
      <div id='headerContent'>
        <div id='LogoHolder'>
        </div>
        <nav id='headerNav'>
          <ul>
            <li id='photoThumbnail'></li>
            <li id='currentUser'>
              <ul class="dropdownMenu">
                <li>link1</li>
                <li>link2</li>
                <li>link3</li>
                <li>link4</li>
                <li>link5</li>
                <li>link6</li>
              </ul>
            </li>
          </ul>
        </nav>
      </div>
    </header>

My JS:

$("#headerNav").click(function(e) {
  return $(".dropdownMenu").addClass('homeMenuClass').toggle()(e.stopPropagation());
});
$("html").click(function(e) {
  return $(".dropDownMenu").addClass('menuGone').toggle();
});

CSS:

.homeMenuClass {
                position:absolute;
            background-color:white;
            border: {
                        top: 2px solid $main-background-color;
                 right: 1px solid $main-background-color;
                 bottom: 1px solid $main-background-color;
                     left: 1px solid $main-background-color;
            }
            top:37px;
            right:-40px;
            height:245px;
            width:200px;
            font-weight:bold;
            color:gray;

            -webkit-box-shadow: 0 10px 6px -6px #777;
            -moz-box-shadow: 0 10px 6px -6px #777;
                        box-shadow: 0 10px 6px -6px #777;

                  a {
                    color:gray;
                  }
}

.menuGone {
    display:none !important;
}

I have looked at similar questions and tried solutions but none seem to work. Kind regards

Update: (This is working 100% the way I want it to but for some reason it feels like I'm going about things the wrong way. eg the addClass('homeMenuClass') line.. Slightly confused about the toggle variable function.

$(function() {
       var button = $("#headerNav"),
    menu = $(".dropdownMenu", button),
    toggle = function(e) {
        e.stopPropagation();
        if (menu.hasClass('open')) {
            menu.toggle().removeClass('open').addClass('homeMenuClass');
        } else {
            menu.toggle().addClass('open').addClass('homeMenuClass');
        }
    };

button.click(toggle);

$(document).click(function(e) {
    if (($(e.target).parents('#headerNav').length === 0 || e.target.id === 'headerNav') && menu.hasClass('open')) {
        toggle.apply(button, [e]);
    }
});

});

Your code looks somewhat weird, try this:

$("#headerNav").click(function(e) {
  $(".dropdownMenu").addClass('homeMenuClass').show();
});
$("html").click(function(e) {
  $(".dropDownMenu").addClass('menuGone').hide();
});

Unsure why you were returning anything from the event handlers and what that last () with preventDefault was about. Also, do you really need to add classes and toggle()? Also, I've switched to show/hide instead of toggle as you'll always want to hide when clicking outside and always show when clicking the menu.

Edit: If the classes are used to show/hide the menu you can (like I said) skip the call to toggle/show/hide and just use add/removeClass, like this:

$("#headerNav").click(function(e) {
  $(".dropdownMenu").removeClass('menuGone').addClass('homeMenuClass');
});
$("html").click(function(e) {
  $(".dropDownMenu").removeClass('homeMenuClass').addClass('menuGone');
});

Edit2: Note that when clicking #headerNav the click will bubble up to html as well unless you do e.stopPropagation(). You could also check in your html click handler if e.target was #headerNav or a child of #headerNav.

Here is a jsfiddle that does what you want.

Important notes

  • I defined the toggle function seperately to be able to reuse it.
  • Added the word 'Test' so I had something to click to open the menu
  • Always pass the event object to the toggle function since it needs to stop propagation to prevent an infinite loop.

The function is layed out as following:

jQuery(function($) {
    "use strict";
    var $menu = $("#headerNav"),
        $dd = $(".dropdownMenu", $menu),
        //Ddefine function for toggling display so we can reuse it
        toggle = function(e) {
            // Stop propagation will prevent the event to 'bubble' up through its parents again
            // In this case it is needed to prevent an eternal loop (well jQuery will return an error at some point but regardless, this is how we prevent it)
            // Normally click events when triggered are called on the direct element under the mouse, and then its parent, and its parent, because triggering one elements effectively triggers all its parents. Since we catch the click event at the highest level already we should prevent this when it is not needed, or when we click the menu itself, the toggle function will be triggered once because it was clicked and another time because at the highest level, document it might be triggered again
            e.stopPropagation();
            if ($dd.hasClass('homeMenuClass')) {
                $dd.hide().removeClass('homeMenuClass');
            } else {
                $dd.show().addClass('homeMenuClass');
            }
        };

    // Simply binds the already made function to the menu click
    $menu.click(toggle);

    // the document is the highest level in the DOM and most events propagate up till here unless stopped before
    $(document).click(function(e) {
        // Here we check if one of the parents of the target or the target itself is #headernav, if it is the function will be triggered by the menu click
        // If #headerNav is not a parent or the target isn't headerNav itself it means that we clicked somewhere outside of it
        if (($(e.target).parents('#headerNav').length === 0 || e.target.id === 'headerNav') && $dd.hasClass('homeMenuClass')) {

            // Here we take the function toggle and apply $menu to it, make $menu `this` in the context of toggle, this isn't really nessacery for this particular case but I think its neater to call the function in the same context as the normal click would have... 
            // e is passed in an array(the brackets make it an array) as the first argument
            // more on that here:http://trephine.org/t/index.php?title=JavaScript_call_and_apply
            toggle.apply($menu, [e]);
        }
    });

});

Edit

Changed the function to use your classes and an instant hide/show -> this is what toggle() does but it's indescriminate of what state your element is in.

Try the onblur() js event.

$("#headerNav").blur(function(e) {
    return $(".dropDownMenu").addClass('menuGone').toggle();
});

try removeClass()

$("html").click(function(e) {
  return $(".dropDownMenu").removeClass('homeMenuClass');
});

Did you try :

$(document).click(function(e) {   
return $(".dropDownMenu").addClass('menuGone').toggle(); 
});

Btw, you could use .on() , which remplace old event handlers : http://api.jquery.com/on/

It may also be a css problem : is your menuGone class applied ?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM