Fare clic fuori dal menu per chiudere in jquery


107

Quindi ho un menu a discesa che viene visualizzato con un clic, secondo i requisiti aziendali. Il menu viene nuovamente nascosto quando ci si allontana con il mouse.

Ma ora mi viene chiesto di mantenerlo in posizione finché l'utente non fa clic in un punto qualsiasi del documento. Come si può ottenere questo risultato?

Questa è una versione semplificata di quello che ho ora:

$(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> 

Ho provato qualcosa di simile $('document[id!=MainOptSubMenu]').click(function()pensando che si sarebbe attivato su tutto ciò che non era nel menu, ma non ha funzionato.



Risposte:


196

Dai un'occhiata all'approccio utilizzato da questa domanda:

Come si rileva un clic all'esterno di un elemento?

Allega un evento clic al corpo del documento che chiude la finestra. Allega un evento clic separato alla finestra che interrompe la propagazione al corpo del documento.
$('html').click(function() {
  //Hide the menus if visible
});

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


16
è molto bello ma dovresti usare $ ('html'). click () non body. Il corpo ha sempre l'altezza del suo contenuto. Non c'è molto contenuto o lo schermo è molto alto, funziona solo nella parte riempita dal corpo. Copia da: stackoverflow.com/questions/152975/… - meo 25 febbraio 11 alle 15:35
NickGreen

ottima soluzione. qualche idea del perché funzioni con .click () e non con .live ('click', func ...?
Hat

3
L'unico problema con questo approccio è che gli oggetti che hanno già un listener di clic che si fermaPropagation per altri motivi non hanno alcun effetto. Capisco perché è così, ma è ancora un limite di questa soluzione.
DanH

53

La risposta è giusta, ma aggiungerà un listener che verrà attivato ogni volta che si verifica un clic sulla tua pagina. Per evitare ciò, puoi aggiungere l'ascoltatore solo per una volta:

$('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);
        }
    });
});

Modifica: se vuoi evitare il stopPropagation()pulsante iniziale puoi usarlo

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');
    }
});

Vedo molto codice come questo, non aggiungerebbe una nuova funzione di clic al documento ogni volta che si fa clic sul collegamento del menu? Quindi, se fai clic sul collegamento del menu 1000 volte senza aggiornare la pagina, avrai 1000 funzioni che occupano memoria e che vengono eseguite?
eselk

Non importa, non ho visto la funzione "one", ora capisco perché funziona: api.jquery.com/one
eselk

2
bella soluzione ma ho dovuto usare e.stopPropagation()in Chrome per impedire che il primo evento si one()
attivasse

18

Le stopPropagationopzioni sono pessime perché possono interferire con altri gestori di eventi, inclusi altri menu che potrebbero avere associati gestori ravvicinati all'elemento HTML.

Ecco una semplice soluzione basata sulla risposta di user2989143 :

$('html').click(function(event) {
    if ($(event.target).closest('#menu-container, #menu-activator').length === 0) {
        $('#menu-container').hide();
    }
});

17

Se l'utilizzo di un plug-in va bene nel tuo caso, suggerisco il plug-in clickoutside di Ben Alman che si trova qui :

il suo utilizzo è semplice come questo:

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

spero che questo ti aiuti.


8

2 opzioni che puoi esaminare:

  • Alla visualizzazione del menu, posiziona un grande DIV vuoto dietro di esso che copre il resto della pagina e dai un evento al clic per chiudere il menu (e se stesso). Questo è simile ai metodi utilizzati con i lightbox in cui facendo clic sullo sfondo si chiude il lightbox
  • Alla visualizzazione del menu, collega un gestore di eventi clic una tantum sul corpo che chiude il menu. Usa '.one ()' di jQuery per questo.

6

Ho trovato una variante della soluzione Grsmto e soluzione di Dennis' risolto il mio problema.

$(".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
        }
    });
});

5

Uso questa soluzione con più elementi con lo stesso comportamento nella stessa pagina:

$("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 () è una cattiva idea, questo rompe il comportamento standard di molte cose, inclusi pulsanti e link.


È fantastico, ma puoi semplificarlo come$target.closest('#menu-container, #menu-activator').length === 0
Code Commander

5

Recentemente ho affrontato lo stesso problema. Ho scritto il codice seguente:

    $('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
     }
    });

Ha funzionato perfettamente per me.


4

che dire di questo?

    $(this).mouseleave(function(){  
        var thisUI = $(this);
        $('html').click(function(){
                thisUI.hide();
            $('html').unbind('click');
         });
     });

3

Trovo più utile utilizzare mousedown-event invece di click-event. L'evento clic non funziona se l'utente fa clic su altri elementi della pagina con eventi clic. In combinazione con il metodo one () di jQuery assomiglia a questo:

$("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();
});

3

anche io mi sono imbattuto nella stessa situazione e uno dei miei mentori mi ha trasmesso questa idea.

passo: 1 quando si fa clic sul pulsante su cui dovremmo mostrare il menu a discesa. quindi aggiungi il nome della classe di seguito "more_wrap_background" alla pagina attiva corrente come mostrato di seguito

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

passaggio 2 quindi aggiungi un clic per il tag div come

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

dove hideDropDown è la funzione da chiamare per nascondere il menu a discesa

Passaggio 3 e passaggio importante mentre si nasconde il menu a discesa è rimuovere quella classe che hai aggiunto in precedenza come

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

Sto rimuovendo usando il suo ID nel codice sopra

.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;
}

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
    }
};

E meglio impostare l'ascoltatore solo quando il menu è visibile e rimuovere sempre l'ascoltatore dopo che il menu è stato nascosto.


1

Penso che tu abbia bisogno di qualcosa del genere: 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();               
            }
      });
  });    
});

Spero ti sia utile!


1

Utilizza il selettore ": visible". Dove .menuitem è l'elemento da nascondere ...

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

O se hai già gli elementi .menuitem in una var ...

var menitems = $('.menuitem');
$('body').click(function(){
  menuitems.filter(':visible').hide('fast');
});

0

Non deve essere complicato.

$(document).on('click', function() {
    $("#menu:not(:hover)").hide();
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.