Cliquez sur le menu extérieur pour fermer dans jquery

107

J'ai donc un menu déroulant qui s'affiche en un clic, selon les exigences de l'entreprise. Le menu redevient masqué lorsque vous vous en éloignez.

Mais maintenant, on me demande de le garder en place jusqu'à ce que l'utilisateur clique n'importe où sur le document. Comment cela peut-il être accompli?

Ceci est une version simplifiée de ce que j'ai maintenant:

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

J'ai essayé quelque chose comme ça en $('document[id!=MainOptSubMenu]').click(function()pensant que cela se déclencherait sur tout ce qui n'était pas le menu, mais cela ne fonctionnait pas.

George
la source

Réponses:

196

Jetez un œil à l'approche utilisée par cette question:

Comment détecter un clic en dehors d'un élément?

Attachez un événement de clic au corps du document qui ferme la fenêtre. Attachez un événement de clic distinct à la fenêtre qui arrête la propagation vers le corps du document.
$('html').click(function() {
  //Hide the menus if visible
});

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

Jon W
la source
16
c'est très beau mais vous devriez utiliser $ ('html'). click () pas body. Le corps a toujours la hauteur de son contenu. S'il n'y a pas beaucoup de contenu ou que l'écran est très haut, cela ne fonctionne que sur la partie remplie par le corps. Copie de: stackoverflow.com/questions/152975/… - meo 25 février 2011 à 15:35
NickGreen
excellente solution. aucune idée pourquoi cela fonctionne avec .click () et pas avec .live ('click', func ...?
Hat
3
Le seul problème avec cette approche est que les objets qui ont déjà un écouteur de clic qui stopPropagation pour d'autres raisons n'ont aucun effet. Je comprends pourquoi il en est ainsi, mais c'est toujours une limitation de cette solution.
DanH
53

La réponse est juste, mais cela ajoutera un auditeur qui se déclenchera à chaque fois qu'un clic se produira sur votre page. Pour éviter cela, vous pouvez ajouter l'auditeur pour une seule fois:

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

Modifier: si vous voulez éviter le stopPropagation()bouton initial, vous pouvez utiliser ceci

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
la source
Je vois beaucoup de code comme celui-ci, est-ce que cela n'ajouterait pas une nouvelle fonction de clic au document à chaque fois que le lien de menu est cliqué? Donc, si vous cliquez sur le lien du menu 1000 fois sans rafraîchir la page, vous aurez 1000 fonctions occupant de la mémoire et également exécutées?
eselk
Nevermind, je n'ai pas vu la fonction "one", maintenant je comprends pourquoi cela fonctionne: api.jquery.com/one
eselk
2
belle solution mais j'ai dû utiliser e.stopPropagation()dans Chrome pour empêcher le premier événement de se déclencher dans le one()gestionnaire
antfx
18

Les stopPropagationoptions sont mauvaises car elles peuvent interférer avec d'autres gestionnaires d'événements, y compris d'autres menus qui pourraient avoir attaché des gestionnaires de fermeture à l'élément HTML.

Voici une solution simple basée sur la réponse de user2989143 :

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

Si l'utilisation d'un plugin vous convient, alors je suggère le plugin clickoutside de Ben Alman situé ici :

son utilisation est aussi simple que ceci:

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

J'espère que cela t'aides.

Hash subliminal
la source
8

2 options que vous pouvez étudier:

  • Lors de l'affichage du menu, placez un grand DIV vide derrière lui couvrant le reste de la page et donnez-lui un événement on-click pour fermer le menu (et lui-même). Cela s'apparente aux méthodes utilisées avec les lightboxes où cliquer sur l'arrière-plan ferme la lightbox
  • Lors de l'affichage du menu, attachez un gestionnaire d'événement de clic unique sur le corps qui ferme le menu. Vous utilisez '.one ()' de jQuery pour cela.
DA.
la source
6

J'ai trouvé une variante de la solution de Grsmto et la solution de Dennis fixe mon problème.

$(".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
la source
5

J'utilise cette solution avec plusieurs éléments avec le même comportement dans la même page:

$("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 () est une mauvaise idée, cela rompt le comportement standard de beaucoup de choses, y compris les boutons et les liens.

user2989143
la source
C'est génial, mais vous pouvez le simplifier comme$target.closest('#menu-container, #menu-activator').length === 0
Code Commander
5

J'ai récemment fait face au même problème. J'ai écrit le code suivant:

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

Cela a parfaitement fonctionné pour moi.

Namish
la source
4

Et ça?

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

Je trouve plus utile d'utiliser mousedown-event au lieu de click-event. L'événement de clic ne fonctionne pas si l'utilisateur clique sur d'autres éléments de la page avec des événements de clic. En combinaison avec la méthode one () de jQuery, cela ressemble à ceci:

$("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
la source
3

même je suis tombé sur la même situation et l'un de mes mentors m'a fait part de cette idée.

étape: 1 lorsque vous cliquez sur le bouton sur lequel nous devons afficher le menu déroulant. puis ajoutez le nom de classe ci-dessous "more_wrap_background" à la page active actuelle comme indiqué ci-dessous

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

étape-2 puis ajoutez un clic pour la balise div comme

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

où hideDropDown est la fonction à appeler pour masquer le menu déroulant

Étape 3 et étape importante lors du masquage du menu déroulant est de supprimer cette classe que vous avez ajoutée plus tôt comme

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

Je supprime en utilisant son identifiant dans le code ci-dessus

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

Et mieux vaut définir l'auditeur uniquement lorsque votre menu est visible et toujours supprimer l'auditeur une fois que le menu est masqué.

Олег Всильдеревьев
la source
1

Je pense que vous avez besoin de quelque chose comme ça: 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();               
            }
      });
  });    
});

J'espère que cela vous sera utile!

Sakata Gintoki
la source
1

Utilisez le sélecteur «: visible». Où .menuitem est le ou les éléments à masquer ...

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

Ou si vous avez déjà le ou les éléments .menuitem dans un var ...

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

Cela n'a pas à être compliqué.

$(document).on('click', function() {
    $("#menu:not(:hover)").hide();
});
Jonathan Gaudé
la source