108 votes

Cliquer à l'extérieur du menu pour le fermer en jquery

J'ai donc un menu déroulant qui s'affiche en un clic, conformément aux exigences de l'entreprise. Le menu redevient caché lorsque vous le quittez avec la souris.

Mais on me demande maintenant de faire en sorte qu'il reste en place jusqu'à ce que l'utilisateur clique n'importe où dans le document. Comment cela peut-il être réalisé ?

C'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 $('document[id!=MainOptSubMenu]').click(function() en pensant que ça se déclencherait sur tout ce qui n'est pas le menu, mais ça n'a pas marché.

0 votes

203voto

Jon W Points 7032

Jetez un coup d'œil à l'approche utilisée pour cette question :

Comment détecter un clic à l'extérieur 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();
});

17 votes

C'est très beau mais vous devriez utiliser $('html').click() et non body. Le corps a toujours la hauteur de son contenu. S'il n'y a pas beaucoup de contenu ou si 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év 11 à 15:35

0 votes

Excellente solution. Savez-vous pourquoi cela fonctionne avec .click() et pas avec .live('click', func... ?

3 votes

Le seul problème avec cette approche est que les objets qui ont déjà un écouteur de clics qui arrêtePropagation pour d'autres raisons n'ont aucun effet. Je comprends pourquoi il en est ainsi, mais c'est quand même une limitation de cette solution.

55voto

Grsmto Points 1357

La réponse est correcte, mais cela ajoutera un écouteur qui sera déclenché chaque fois qu'un clic se produira sur votre page. Pour éviter cela, vous pouvez ajouter le listener 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);
        }
    });
});

Edit : si vous voulez éviter le stopPropagation() sur le 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');
    }
});

0 votes

Je vois souvent ce genre de code, mais cela n'ajouterait-il pas une nouvelle fonction de clic au document chaque fois que le lien du menu est cliqué ? Donc, si vous cliquez sur le lien du menu 1000 fois sans rafraîchir la page, vous aurez 1000 fonctions qui occuperont la mémoire et s'exécuteront également ?

0 votes

Nevermind, je n'avais pas vu la fonction "un", maintenant je comprends pourquoi cela fonctionne : api.jquery.com/one

2 votes

Bonne solution mais j'ai dû utiliser e.stopPropagation() dans Chrome afin d'empêcher le premier événement de se déclencher dans l'environnement de l'utilisateur. one() manipulateur

26voto

Code Commander Points 1352

Le site stopPropagation 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 utilisateur2989143 La réponse de la Commission :

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

17voto

Emin Points 5516

Si l'utilisation d'un plugin est acceptable dans votre cas, je vous suggère le plugin clickoutside de Ben Alman, qui se trouve à l'adresse suivante ici :

son utilisation est aussi simple que cela :

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

J'espère que cela vous aidera.

9voto

DA. Points 15714

Deux options que vous pouvez étudier :

  • Lorsque le menu est affiché, placez un grand DIV vide derrière lui pour couvrir le reste de la page et donnez-lui un événement on-click pour fermer le menu (et lui-même). Cette méthode s'apparente à celle utilisée pour les boîtes à lumière, où un clic sur l'arrière-plan ferme la boîte à lumière.
  • Lors de l'affichage du menu, attachez un gestionnaire d'événement de clic unique sur le corps qui ferme le menu. Pour cela, utilisez la fonction '.one()' de jQuery.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X