Kliknij menu zewnętrzne, aby zamknąć w jquery

107

Mam więc menu rozwijane, które wyświetla się po kliknięciu, zgodnie z wymaganiami biznesowymi. Menu zostanie ponownie ukryte, gdy odejdziesz od niego myszką.

Ale teraz jestem proszony o pozostawienie go na miejscu, dopóki użytkownik nie kliknie w dowolnym miejscu dokumentu. Jak można to osiągnąć?

To jest uproszczona wersja tego, co mam teraz:

$(document).ready(function() {
  $("ul.opMenu li").click(function(){
   $('#MainOptSubMenu',this).css('visibility', 'visible');
  });

  $("ul.opMenu li").mouseleave(function(){
      $('#MainOptSubMenu',this).css('visibility', 'hidden');
  });
});



<ul  class="opMenu">
  <li id="footwo" class="">
    <span id="optImg" style="display: inline-block;"> <img src="http://localhost.vmsinfo.com:8002/insight/images/options-hover2.gif"/> </span>
      <ul id="MainOptSubMenu" style="visibility: hidden; top: 25px; border-top: 0px solid rgb(217, 228, 250); background-color: rgb(217, 228, 250); padding-bottom: 15px;">
        <li>some</li>
       <li>nav</li>
       <li>links</li>
       </ul>
    </li>
</ul> 

Próbowałem czegoś takiego, $('document[id!=MainOptSubMenu]').click(function()myśląc, że uruchomi to wszystko, co nie jest w menu, ale nie zadziałało.

Jerzy
źródło

Odpowiedzi:

196

Spójrz na podejście zastosowane w tym pytaniu:

Jak wykryć kliknięcie poza elementem?

Dołącz zdarzenie click do treści dokumentu, co zamyka okno. Dołącz osobne zdarzenie kliknięcia do okna, które zatrzymuje propagację do treści dokumentu.
$('html').click(function() {
  //Hide the menus if visible
});

$('#menucontainer').click(function(event){
    event.stopPropagation();
});

Jon W
źródło
16
jest bardzo piękny, ale powinieneś używać $ ('html'). click (), a nie body. Ciało zawsze ma wysokość swojej zawartości. Jeśli treści jest niewiele lub ekran jest bardzo wysoki, działa tylko na części wypełnionej ciałem. Skopiuj z: stackoverflow.com/questions/152975/… - meo 25 lutego '11 o 15:35
NickGreen
świetne rozwiązanie. masz jakiś pomysł, dlaczego działa z .click (), a nie z .live ('click', func ...?
Hat
3
Jedynym problemem związanym z tym podejściem jest to, że obiekty, które mają już nasłuchiwanie kliknięć, które zatrzymują propagację z innych powodów, nie działają. Rozumiem, dlaczego tak jest, ale nadal jest to ograniczenie tego rozwiązania.
DanH
53

Odpowiedź jest prawidłowa, ale doda odbiornik, który będzie uruchamiany za każdym razem, gdy nastąpi kliknięcie na Twojej stronie. Aby tego uniknąć, możesz dodać odbiorcę tylko raz:

$('a#menu-link').on('click', function(e) {
    e.preventDefault();
    e.stopPropagation();

    $('#menu').toggleClass('open');

    $(document).one('click', function closeMenu (e){
        if($('#menu').has(e.target).length === 0){
            $('#menu').removeClass('open');
        } else {
            $(document).one('click', closeMenu);
        }
    });
});

Edytuj: jeśli chcesz uniknąć stopPropagation()przycisku początkowego, możesz tego użyć

var $menu = $('#menu');

$('a#menu-link').on('click', function(e) {
    e.preventDefault();

    if (!$menu.hasClass('active')) {
        $menu.addClass('active');

        $(document).one('click', function closeTooltip(e) {
            if ($menu.has(e.target).length === 0 && $('a#menu-link').has(e.target).length === 0) {
                $menu.removeClass('active');
            } else if ($menu.hasClass('active')) {
                $(document).one('click', closeTooltip);
            }
        });
    } else {
        $menu.removeClass('active');
    }
});
adriendenat
źródło
Często widzę taki kod, czy nie spowodowałoby to dodania nowej funkcji kliknięcia do dokumentu za każdym razem, gdy kliknięto łącze menu? Więc jeśli klikniesz link menu 1000 razy bez odświeżania strony, będziesz mieć 1000 funkcji zajmujących pamięć, a także wykonywanych?
eselk
Nieważne, nie widziałem funkcji „jeden”, teraz rozumiem, dlaczego to działa: api.jquery.com/one
eselk
2
fajne rozwiązanie, ale musiałem użyć e.stopPropagation()w Chrome, aby zatrzymać pierwsze zdarzenie od odpalenia w one()
module
18

Te stopPropagationopcje są złe, ponieważ mogą one kolidować z innymi obsługi zdarzeń oraz innych menu, które mogły załączonych bliskie Wozy do elementu HTML.

Oto proste rozwiązanie oparte na user2989143 jest odpowiedź:

$('html').click(function(event) {
    if ($(event.target).closest('#menu-container, #menu-activator').length === 0) {
        $('#menu-container').hide();
    }
});
Code Commander
źródło
17

Jeśli w twoim przypadku użycie wtyczki jest w porządku, proponuję wtyczkę clickoutside Bena Almana znajdującą się tutaj :

jego użycie jest tak proste, jak to:

$('#menu').bind('clickoutside', function (event) {
    $(this).hide();
});

mam nadzieję że to pomoże.

Hash podprogowy
źródło
8

2 opcje, które możesz zbadać:

  • Po wyświetleniu menu umieść duży pusty DIV za nim zakrywający resztę strony i nadaj temu zdarzeniu po kliknięciu, aby zamknąć menu (i siebie). Jest to podobne do metod stosowanych w lightboxach, w których kliknięcie tła zamyka lightbox
  • Podczas wyświetlania menu dołącz moduł obsługi zdarzeń jednorazowego kliknięcia do treści, która zamyka menu. Używasz do tego '.one ()' jQuery.
DA.
źródło
6

Znalazłem wariant rozwiązania Grsmto, a rozwiązanie Dennisa rozwiązało mój problem.

$(".MainNavContainer").click(function (event) {
    //event.preventDefault();  // Might cause problems depending on implementation
    event.stopPropagation();

    $(document).one('click', function (e) {
        if(!$(e.target).is('.MainNavContainer')) {
            // code to hide menus
        }
    });
});
cat5dev
źródło
5

Używam tego rozwiązania z wieloma elementami o tym samym zachowaniu na tej samej stronie:

$("html").click(function(event){
    var otarget = $(event.target);
    if (!otarget.parents('#id_of element').length && otarget.attr('id')!="id_of element" && !otarget.parents('#id_of_activator').length) {
        $('#id_of element').hide();
    }
});

stopPropagation () to zły pomysł, który łamie standardowe zachowanie wielu rzeczy, w tym przycisków i linków.

user2989143
źródło
To jest świetne, ale możesz to uprościć, ponieważ$target.closest('#menu-container, #menu-activator').length === 0
Code Commander
5

Niedawno stanąłem przed tym samym problemem. Napisałem następujący kod:

    $('html').click(function(e) {
      var a = e.target;
      if ($(a).parents('.menu_container').length === 0) {
        $('.ofSubLevelLinks').removeClass('active'); //hide menu item
        $('.menu_container li > img').hide(); //hide dropdown image, if any
     }
    });

U mnie zadziałało doskonale.

Namisz
źródło
4

a co z tym?

    $(this).mouseleave(function(){  
        var thisUI = $(this);
        $('html').click(function(){
                thisUI.hide();
            $('html').unbind('click');
         });
     });
cgfix
źródło
3

Uważam, że bardziej przydatne jest użycie zdarzenia kliknięcia myszy zamiast zdarzenia kliknięcia. Zdarzenie kliknięcia nie działa, jeśli użytkownik kliknie inne elementy na stronie ze zdarzeniami kliknięcia. W połączeniu z metodą one () jQuery wygląda to tak:

$("ul.opMenu li").click(function(event){

   //event.stopPropagation(); not required any more
   $('#MainOptSubMenu').show();

   // add one mousedown event to html
   $('html').one('mousedown', function(){
       $('#MainOptSubMenu').hide();
   });
});

// mousedown must not be triggered inside menu
$("ul.opMenu li").bind('mousedown', function(evt){
    evt.stopPropagation();
});
yeeno
źródło
3

nawet ja natknąłem się na tę samą sytuację i jeden z moich mentorów przedstawił mi ten pomysł.

krok: 1 po kliknięciu na przycisk, na którym powinniśmy wyświetlić rozwijane menu. następnie dodaj poniższą nazwę klasy „more_wrap_background” do bieżącej aktywnej strony, jak pokazano poniżej

$('.ui-page-active').append("<div class='more_wrap_background' id='more-wrap-bg'> </div>");

krok 2, a następnie dodaj kliknięcia znacznika DIV, na przykład

$(document).on('click', '#more-wrap-bg', hideDropDown);

gdzie hideDropDown jest funkcją, która ma zostać wywołana w celu ukrycia menu rozwijanego

Krok-3 i ważny krok podczas ukrywania menu rozwijanego polega na usunięciu tej klasy, którą dodałeś wcześniej, jak

$('#more-wrap-bg').remove();

Usuwam używając jego id w powyższym kodzie

.more_wrap_background {
  top: 0;
  padding: 0;
  margin: 0;
  background: rgba(0, 0, 0, 0.1);
  position: fixed;
  display: block;
  width: 100% !important;
  z-index: 999;//should be one less than the drop down menu's z-index
  height: 100% !important;
}
Abhimaan
źródło
2
$("html").click( onOutsideClick );
onOutsideClick = function( e )
{
    var t = $( e.target );
    if ( !(
        t.is("#mymenu" ) ||     //Where #mymenu - is a div container of your menu
        t.parents( "#mymenu" ).length > 0
        )   )
    {
        //TODO: hide your menu
    }
};

Lepiej jest ustawiać nasłuchiwanie tylko wtedy, gdy menu jest widoczne i zawsze usuwać nasłuchiwanie, gdy menu zostanie ukryte.

Олег Всильдеревьев
źródło
1

Myślę, że potrzebujesz czegoś takiego: http://jsfiddle.net/BeenYoung/BXaqW/3/

$(document).ready(function() {
  $("ul.opMenu li").each(function(){
      $(this).click(function(){
            if($(this).hasClass('opened')==false){          
                $('.opMenu').find('.opened').removeClass('opened').find('ul').slideUp();
                $(this).addClass('opened'); 
                $(this).find("ul").slideDown();
            }else{
                $(this).removeClass('opened'); 
                $(this).find("ul").slideUp();               
            }
      });
  });    
});

Mam nadzieję, że to dla Ciebie przydatne!

Sakata Gintoki
źródło
1

Użyj selektora „: visible”. Gdzie .menuitem to element (y) do ukrycia ...

$('body').click(function(){
  $('.menuitem:visible').hide('fast');
});

Lub jeśli masz już elementy .menuitem w var ...

var menitems = $('.menuitem');
$('body').click(function(){
  menuitems.filter(':visible').hide('fast');
});
ekerner
źródło
0

To nie musi być skomplikowane.

$(document).on('click', function() {
    $("#menu:not(:hover)").hide();
});
Jonathan Gaudé
źródło