web-dev-qa-db-fra.com

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

J'ai quelques menus HTML, que je montre complètement quand un utilisateur clique sur la tête de ces menus. Je souhaite masquer ces éléments lorsque l'utilisateur clique en dehors de la zone des menus.

Est-ce que quelque chose comme ceci est possible avec jQuery?

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});
2179
Sergio del Amo

REMARQUE: l'utilisation de stopEventPropagation() doit être évitée car elle interrompt le flux d'événements normal dans le DOM. Voir cet article pour plus d'informations. Pensez à utiliser cette méthode à la place

Attachez un événement de clic au corps du document qui ferme la fenêtre. Attachez au conteneur un événement click distinct qui arrête la propagation vers le corps du document.

$(window).click(function() {
//Hide the menus if visible
});

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

Vous pouvez écouter un événement click sur document, puis vous assurer que #menucontainer n'est pas un ancêtre ni la cible de l'élément sur lequel vous avez cliqué en utilisant .closest() .

Si ce n'est pas le cas, l'élément sur lequel vous avez cliqué est en dehors du #menucontainer et vous pouvez le masquer en toute sécurité.

$(document).click(function(event) { 
  $target = $(event.target);
  if(!$target.closest('#menucontainer').length && 
  $('#menucontainer').is(":visible")) {
    $('#menucontainer').hide();
  }        
});

Edit - 2017-06-23

Vous pouvez également nettoyer après l'écouteur d'événements si vous envisagez de fermer le menu et de ne plus écouter les événements. Cette fonction nettoie que le programme d'écoute nouvellement créé, en préservant les autres programmes d'écoute de clic sur document. Avec la syntaxe ES2015:

export function hideOnClickOutside(selector) {
  const outsideClickListener = (event) => {
    $target = $(event.target);
    if (!$target.closest(selector).length && $(selector).is(':visible')) {
        $(selector).hide();
        removeClickListener();
    }
  }

  const removeClickListener = () => {
    document.removeEventListener('click', outsideClickListener)
  }

  document.addEventListener('click', outsideClickListener)
}

Modifier - 2018-03-11

Pour ceux qui ne veulent pas utiliser jQuery. Voici le code ci-dessus en clair vanillaJS (ECMAScript6).

function hideOnClickOutside(element) {
    const outsideClickListener = event => {
        if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
          element.style.display = 'none'
          removeClickListener()
        }
    }

    const removeClickListener = () => {
        document.removeEventListener('click', outsideClickListener)
    }

    document.addEventListener('click', outsideClickListener)
}

const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ) // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js 

NOTE: Ceci est basé sur le commentaire Alex d’utiliser simplement !element.contains(event.target) au lieu de la partie jQuery.

Mais element.closest() est maintenant disponible dans tous les principaux navigateurs (la version du W3C est légèrement différente de celle de jQuery) . Polyfills est disponible ici: https://developer.mozilla.org/en-US/docs/ Web/API/Elément/le plus proche

1228
Art

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

La raison pour laquelle cette question est si populaire et a tant de réponses est qu’elle est trompeusement complexe. Après presque huit ans et des dizaines de réponses, je suis vraiment surpris de constater à quel point l’accessibilité a été négligée.

Je souhaite masquer ces éléments lorsque l'utilisateur clique en dehors de la zone des menus.

Ceci est une noble cause et constitue le problème actuel . Le titre de la question - ce que la plupart des réponses semblent tenter de résoudre - contient un malheur redoutable.

Astuce: c'est le mot "cliquez" !

Vous ne voulez pas réellement lier les gestionnaires de clics.

Si vous liez des gestionnaires qui cliquent pour fermer la boîte de dialogue, vous avez déjà échoué. La raison de votre échec est que tout le monde ne déclenche pas les événements click. Les utilisateurs qui n'utilisent pas de souris pourront échapper à votre boîte de dialogue (et votre menu contextuel est sans doute un type de boîte de dialogue) en appuyant sur Tabet ils ne pourront alors pas lire le contenu derrière la boîte de dialogue sans déclencher ultérieurement un événement click.

Alors reformulons la question.

Comment ferme-t-on une boîte de dialogue lorsqu'un utilisateur en a fini?

C'est le but. Malheureusement, nous devons maintenant lier l'événement userisfinishedwiththedialog, et cette liaison n'est pas si simple.

Alors, comment pouvons-nous détecter qu'un utilisateur a fini d'utiliser un dialogue?

focusout événement

Un bon début consiste à déterminer si le focus est sorti de la boîte de dialogue.

Astuce: soyez prudent avec l'événement blur, blur ne se propage pas si l'événement était lié à la phase de bullage!

jQuery's focusout fera très bien l'affaire. Si vous ne pouvez pas utiliser jQuery, vous pouvez utiliser blur pendant la phase de capture:

element.addEventListener('blur', ..., true);
//                       use capture: ^^^^

De plus, pour de nombreuses boîtes de dialogue, vous devez autoriser le conteneur à se concentrer. Ajoutez tabindex="-1" pour permettre à la boîte de dialogue de recevoir le focus de manière dynamique sans interrompre le flux de tabulation.

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on('focusout', function () {
  $(this).removeClass('active');
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

Si vous jouez avec cette démo pendant plus d'une minute, vous devriez rapidement commencer à voir les problèmes.

La première est que le lien dans la boîte de dialogue n'est pas cliquable. Si vous tentez de cliquer dessus ou d'y accéder, la boîte de dialogue se fermera avant l'interaction. En effet, la focalisation de l'élément interne déclenche un événement focusout avant de déclencher à nouveau un événement focusin.

Le correctif consiste à mettre en file d'attente le changement d'état sur la boucle d'événement. Cela peut être fait en utilisant setImmediate(...) ou setTimeout(..., 0) pour les navigateurs qui ne prennent pas en charge setImmediate. Une fois mis en file d'attente, il peut être annulé par un focusin suivant:

$('.submenu').on({
  focusout: function (e) {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function (e) {
    clearTimeout($(this).data('submenuTimer'));
  }
});
$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

Le deuxième problème est que la boîte de dialogue ne se fermera pas lorsque le lien sera à nouveau appuyé. En effet, la boîte de dialogue perd le focus, ce qui déclenche le comportement de fermeture, après quoi le clic de lien déclenche la réouverture de la boîte de dialogue.

Comme dans le précédent numéro, l’état d’intervention doit être géré. Étant donné que le changement d'état a déjà été mis en file d'attente, il ne s'agit que de gérer les événements de focus sur les déclencheurs de dialogue:

Cela devrait sembler familier
$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});
$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

Esc clé

Si vous pensiez avoir terminé en gérant les états prioritaires, vous pouvez faire davantage pour simplifier l'expérience utilisateur.

Il s’agit souvent d’une fonctionnalité intéressante, mais il est courant que lorsque vous avez un modal ou un popup de quelque sorte que ce Esc la clé le fermera.

keydown: function (e) {
  if (e.which === 27) {
    $(this).removeClass('active');
    e.preventDefault();
  }
}
$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('active');
      e.preventDefault();
    }
  }
});

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

Si vous savez que la boîte de dialogue contient des éléments activables, vous n'avez pas besoin de la centrer directement. Si vous construisez un menu, vous pouvez plutôt vous concentrer sur le premier élément de menu.

click: function (e) {
  $(this.hash)
    .toggleClass('submenu--active')
    .find('a:first')
    .focus();
  e.preventDefault();
}
$('.menu__link').on({
  click: function (e) {
    $(this.hash)
      .toggleClass('submenu--active')
      .find('a:first')
      .focus();
    e.preventDefault();
  },
  focusout: function () {
    $(this.hash).data('submenuTimer', setTimeout(function () {
      $(this.hash).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('submenuTimer'));  
  }
});

$('.submenu').on({
  focusout: function () {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('submenuTimer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('submenu--active');
      e.preventDefault();
    }
  }
});
.menu {
  list-style: none;
  margin: 0;
  padding: 0;
}
.menu:after {
  clear: both;
  content: '';
  display: table;
}
.menu__item {
  float: left;
  position: relative;
}

.menu__link {
  background-color: lightblue;
  color: black;
  display: block;
  padding: 0.5em 1em;
  text-decoration: none;
}
.menu__link:hover,
.menu__link:focus {
  background-color: black;
  color: lightblue;
}

.submenu {
  border: 1px solid black;
  display: none;
  left: 0;
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  top: 100%;
}
.submenu--active {
  display: block;
}

.submenu__item {
  width: 150px;
}

.submenu__link {
  background-color: lightblue;
  color: black;
  display: block;
  padding: 0.5em 1em;
  text-decoration: none;
}

.submenu__link:hover,
.submenu__link:focus {
  background-color: black;
  color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="menu">
  <li class="menu__item">
    <a class="menu__link" href="#menu-1">Menu 1</a>
    <ul class="submenu" id="menu-1" tabindex="-1">
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
    </ul>
  </li>
  <li class="menu__item">
    <a  class="menu__link" href="#menu-2">Menu 2</a>
    <ul class="submenu" id="menu-2" tabindex="-1">
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
    </ul>
  </li>
</ul>
lorem ipsum <a href="http://example.com/">dolor</a> sit amet.

Rôles WAI-ARIA et autres supports d'accessibilité

Nous espérons que cette réponse couvre les bases de la prise en charge accessible du clavier et de la souris pour cette fonctionnalité, mais comme elle est déjà assez volumineuse, je vais éviter toute discussion sur rôles et attributs de WAI-ARIA , cependant je hautement recommande aux implémenteurs de se référer à la spécification pour savoir quels rôles ils doivent utiliser et tout autre attribut approprié.

248
zzzzBov

Les autres solutions ici ne fonctionnaient pas pour moi alors je devais utiliser:

if(!$(event.target).is('#foo'))
{
    // hide menu
}
133
Dennis

J'ai une application qui fonctionne de la même manière que l'exemple d'Eran, sauf que j'attache l'événement click au corps lorsque j'ouvre le menu ... Un peu comme ceci:

$('#menucontainer').click(function(event) {
  $('html').one('click',function() {
    // Hide the menus
  });

  event.stopPropagation();
});

Plus d'informations sur Fonction one() de jQuery

122
Joe Lencioni
$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

Ça marche très bien pour moi.

36
user212621

Maintenant, il existe un plugin pour cela: événements extérieurs ( blog post )

Ce qui suit se produit lorsqu'un gestionnaire clickoutside (WLOG) est lié à un élément:

  • l'élément est ajouté à un tableau contenant tous les éléments avec les gestionnaires clickoutside
  • a ( namespaced ) click handler is bound to the document (if not already there)
  • sur n'importe quel click du document, l'événement clickoutside est déclenché pour les éléments de ce tableau qui ne sont pas égaux à ou qui sont des parents de la cible click -events
  • en outre, event.target pour l'événement clickoutside est défini sur l'élément sur lequel l'utilisateur a cliqué (vous savez même sur quoi l'utilisateur a cliqué, pas seulement sur l'extérieur).

Ainsi, aucun événement n'est arrêté et les gestionnaires click supplémentaires peuvent être utilisés "au-dessus" de l'élément avec le gestionnaire extérieur.

35
Wolfram

Après des recherches, j'ai trouvé trois solutions de travail (j'ai oublié les liens de page pour référence)

Première solution

<script>
    //The good thing about this solution is it doesn't stop event propagation.

    var clickFlag = 0;
    $('body').on('click', function () {
        if(clickFlag == 0) {
            console.log('hide element here');
            /* Hide element here */
        }
        else {
            clickFlag=0;
        }
    });
    $('body').on('click','#testDiv', function (event) {
        clickFlag = 1;
        console.log('showed the element');
        /* Show the element */
    });
</script>

Deuxième solution

<script>
    $('body').on('click', function(e) {
        if($(e.target).closest('#testDiv').length == 0) {
           /* Hide dropdown here */
        }
    });
</script>

Troisième solution

<script>
    var specifiedElement = document.getElementById('testDiv');
    document.addEventListener('click', function(event) {
        var isClickInside = specifiedElement.contains(event.target);
        if (isClickInside) {
          console.log('You clicked inside')
        }
        else {
          console.log('You clicked outside')
        }
    });
</script>
32
Rameez Rami

Cela a fonctionné pour moi parfaitement !!

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});
29
srinath

Je ne pense pas que ce dont vous avez vraiment besoin est de fermer le menu lorsque l'utilisateur clique à l'extérieur; vous avez besoin que le menu se ferme lorsque l'utilisateur clique n'importe où sur la page. Si vous cliquez sur le menu, ou en dehors du menu, il devrait fermer correctement? 

Ne pas trouver de réponses satisfaisantes ci-dessus m'a incité à écrire cet article de blog l'autre jour. Pour les plus pédants, il y a plusieurs pièges à prendre en compte: 

  1. Si vous attachez un gestionnaire d'événements click à l'élément body au moment du clic, veillez à attendre le deuxième clic avant de fermer le menu et de dissocier l'événement. Sinon, l'événement de clic qui a ouvert le menu s'affichera à l'attention de l'écouteur qui doit fermer le menu.
  2. Si vous utilisez event.stopPropogation () sur un événement click, aucun autre élément de votre page ne peut avoir une fonctionnalité de clic n'importe où pour fermer.
  3. Associer un gestionnaire d'événements click à l'élément body indéfiniment n'est pas une solution performante
  4. La comparaison de la cible de l'événement et de ses parents avec le créateur du gestionnaire suppose que vous souhaitez fermer le menu lorsque vous cliquez dessus, lorsque vous souhaitez réellement le fermer lorsque vous cliquez n'importe où sur la page.
  5. L'écoute d'événements sur l'élément body rendra votre code plus fragile. Une apparence aussi innocente que possible: body { margin-left:auto; margin-right: auto; width:960px;}
24
34m0

Comme l'a dit une autre affiche, il y a beaucoup de pièges, en particulier si l'élément que vous affichez (dans ce cas un menu) a des éléments interactifs . J'ai trouvé la méthode suivante assez robuste:

$('#menuscontainer').click(function(event) {
    //your code that shows the menus fully

    //now set up an event listener so that clicking anywhere outside will close the menu
    $('html').click(function(event) {
        //check up the tree of the click target to check whether user has clicked outside of menu
        if ($(event.target).parents('#menuscontainer').length==0) {
            // your code to hide menu

            //this event listener has done its job so we can unbind it.
            $(this).unbind(event);
        }

    })
});
23
benb

Une solution simple à la situation est la suivante:

$(document).mouseup(function (e)
{
    var container = $("YOUR SELECTOR"); // Give you class or ID

    if (!container.is(e.target) &&            // If the target of the click is not the desired div or section
        container.has(e.target).length === 0) // ... nor a descendant-child of the container
    {
        container.hide();
    }
});

Le script ci-dessus masquera la div si l'événement de clic div est déclenché.

Vous pouvez consulter le blog suivant pour plus d'informations: http://www.codecanal.com/detect-click-outside-div-using-javascript/

20
Jitendra Damor

Solution1

Au lieu d'utiliser event.stopPropagation () qui peut avoir des effets secondaires, définissez simplement une variable indicateur et ajoutez une condition if. J'ai testé cela et fonctionné correctement sans aucun effet secondaire de stopPropagation:

var flag = "1";
$('#menucontainer').click(function(event){
    flag = "0"; // flag 0 means click happened in the area where we should not do any action
});

$('html').click(function() {
    if(flag != "0"){
        // Hide the menus if visible
    }
    else {
        flag = "1";
    }
});

Solution2

Avec juste une simple condition if:

$(document).on('click', function(event){
    var container = $("#menucontainer");
    if (!container.is(event.target) &&            // If the target of the click isn't the container...
        container.has(event.target).length === 0) // ... nor a descendant of the container
    {
        // Do whatever you want to do when click is outside the element
    }
});
18
Iman Sedighi

Vérifiez la cible d'événement de clic de la fenêtre (elle devrait se propager à la fenêtre, tant qu'elle n'est pas capturée ailleurs), et assurez-vous que ce n'est pas l'un des éléments de menu. Si ce n'est pas le cas, vous êtes en dehors de votre menu.

Ou vérifiez la position du clic et voyez si elle est contenue dans la zone du menu.

18
Chris MacDonald

J'ai eu du succès avec quelque chose comme ça:

var $menuscontainer = ...;

$('#trigger').click(function() {
  $menuscontainer.show();

  $('body').click(function(event) {
    var $target = $(event.target);

    if ($target.parents('#menuscontainer').length == 0) {
      $menuscontainer.hide();
    }
  });
});

La logique est la suivante: lorsque #menuscontainer est affiché, liez un gestionnaire de clic au corps qui masque #menuscontainer uniquement si la cible (du clic) n'en est pas un enfant.

15
Chu Yeow

En variante:

var $menu = $('#menucontainer');
$(document).on('click', function (e) {

    // If element is opened and click target is outside it, hide it
    if ($menu.is(':visible') && !$menu.is(e.target) && !$menu.has(e.target).length) {
        $menu.hide();
    }
});

Il n’ya aucun problème avec en stoppant la propagation d’événements et supporte mieux plusieurs menus sur la même page où cliquer sur un deuxième menu alors qu’un premier est ouvert laissera le premier ouvert dans la solution stopPropagation.

14
Bohdan Lyzanets

J'ai trouvé cette méthode dans un plugin d'agenda jQuery.

function ClickOutsideCheck(e)
{
  var el = e.target;
  var popup = $('.popup:visible')[0];
  if (popup==undefined)
    return true;

  while (true){
    if (el == popup ) {
      return true;
    } else if (el == document) {
      $(".popup").hide();
      return false;
    } else {
      el = $(el).parent()[0];
    }
  }
};

$(document).bind('mousedown.popup', ClickOutsideCheck);
12
nazar kuliyev

L'événement a une propriété appelée event.path de l'élément qui est une "liste ordonnée statique de tous ses ancêtres dans l'ordre de l'arbre" . Pour vérifier si un événement provient d'un élément DOM spécifique ou de l'un de ses enfants, il vous suffit de vérifier le chemin d'accès à cet élément DOM spécifique. Il peut également être utilisé pour vérifier plusieurs éléments en effectuant logiquement la transformation OR de l'élément dans la fonction some.

$("body").click(function() {
  target = document.getElementById("main");
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  })
  if (flag) {
    console.log("Inside")
  } else {
    console.log("Outside")
  }
});
#main {
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
  <ul>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
  </ul>
</div>
<div id="main2">
  Outside Main
</div>

Donc, pour votre cas, il devrait être 

$("body").click(function() {
  target = $("#menuscontainer")[0];
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  });
  if (!flag) {
    // Hide the menus
  }
});
10
Dan Philip

Voici la solution JavaScript Vanilla pour les futurs téléspectateurs.

Lorsque vous cliquez sur un élément du document, si son ID est basculé ou si l'élément masqué n'est pas masqué et que l'élément masqué ne contient pas l'élément cliqué, basculez-le.

(function () {
    "use strict";
    var hidden = document.getElementById('hidden');
    document.addEventListener('click', function (e) {
        if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
    }, false);
})();

(function () {
    "use strict";
    var hidden = document.getElementById('hidden');
    document.addEventListener('click', function (e) {
        if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
    }, false);
})();
<a href="javascript:void(0)" id="toggle">Toggle Hidden Div</a>
<div id="hidden" style="display: none;">This content is normally hidden. click anywhere other than this content to make me disappear</div>

Si vous allez avoir plusieurs bascules sur la même page, vous pouvez utiliser quelque chose comme ceci:

  1. Ajoutez le nom de classe hidden à l'élément compressible.
  2. Lorsque vous cliquez sur un document, fermez tous les éléments masqués qui ne contiennent pas l’élément sélectionné et ne sont pas masqués.
  3. Si l'élément sur lequel vous avez cliqué est une bascule, basculez l'élément spécifié.

(function () {
    "use strict";
    var hiddenItems = document.getElementsByClassName('hidden'), hidden;
    document.addEventListener('click', function (e) {
        for (var i = 0; hidden = hiddenItems[i]; i++) {
            if (!hidden.contains(e.target) && hidden.style.display != 'none')
                hidden.style.display = 'none';
        }
        if (e.target.getAttribute('data-toggle')) {
            var toggle = document.querySelector(e.target.getAttribute('data-toggle'));
            toggle.style.display = toggle.style.display == 'none' ? 'block' : 'none';
        }
    }, false);
})();
<a href="javascript:void(0)" data-toggle="#hidden1">Toggle Hidden Div</a>
<div class="hidden" id="hidden1" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden2">Toggle Hidden Div</a>
<div class="hidden" id="hidden2" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden3">Toggle Hidden Div</a>
<div class="hidden" id="hidden3" style="display: none;" data-hidden="true">This content is normally hidden</div>

10
Tiny Giant

Au lieu d'utiliser une interruption de flux, un événement de flou/focus ou toute autre technique délicate, associez simplement le flux d'événement à la parenté de l'élément:

$(document).on("click.menu-outside", function(event){
    // Test if target and it's parent aren't #menuscontainer
    // That means the click event occur on other branch of document tree
    if(!$(event.target).parents().andSelf().is("#menuscontainer")){
        // Click outisde #menuscontainer
        // Hide the menus (but test if menus aren't already hidden)
    }
});

Pour supprimer un écouteur d'événements en dehors d'un clic, procédez comme suit:

$(document).off("click.menu-outside");
8
mems

Si vous utilisez des scripts pour IE et FF 3. * et que vous souhaitez simplement savoir si le clic a eu lieu dans une zone donnée, vous pouvez également utiliser les éléments suivants:

this.outsideElementClick = function(objEvent, objElement){   
var objCurrentElement = objEvent.target || objEvent.srcElement;
var blnInsideX = false;
var blnInsideY = false;

if (objCurrentElement.getBoundingClientRect().left >= objElement.getBoundingClientRect().left && objCurrentElement.getBoundingClientRect().right <= objElement.getBoundingClientRect().right)
    blnInsideX = true;

if (objCurrentElement.getBoundingClientRect().top >= objElement.getBoundingClientRect().top && objCurrentElement.getBoundingClientRect().bottom <= objElement.getBoundingClientRect().bottom)
    blnInsideY = true;

if (blnInsideX && blnInsideY)
    return false;
else
    return true;}
8
Erik

Utilisation:

var go = false;
$(document).click(function(){
    if(go){
        $('#divID').hide();
        go = false;
    }
})

$("#divID").mouseover(function(){
    go = false;
});

$("#divID").mouseout(function (){
    go = true;
});

$("btnID").click( function(){
    if($("#divID:visible").length==1)
        $("#divID").hide(); // Toggle
    $("#divID").show();
});
7
webenformasyon

Je suis surpris que personne n'ait reconnu l'événement focusout:

var button = document.getElementById('button');
button.addEventListener('click', function(e){
  e.target.style.backgroundColor = 'green';
});
button.addEventListener('focusout', function(e){
  e.target.style.backgroundColor = '';
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <button id="button">Click</button>
</body>
</html>

6
Jovanni G

Accrochez un écouteur d'événement de clic sur le document. Dans l'écouteur d'événements, vous pouvez consulter l'objet événement , en particulier le event.target pour voir sur quel élément a été cliqué:

$(document).click(function(e){
    if ($(e.target).closest("#menuscontainer").length == 0) {
        // .closest can help you determine if the element 
        // or one of its ancestors is #menuscontainer
        console.log("hide");
    }
});
6
Salman A
$(document).click(function() {
    $(".overlay-window").hide();
});
$(".overlay-window").click(function() {
    return false;
});

Si vous cliquez sur le document, masquez un élément donné, sauf si vous cliquez sur ce même élément.

5
Rowan

Pour une utilisation plus facile et un code plus expressif, j'ai créé un plugin jQuery pour cela:

$('div.my-element').clickOut(function(target) { 
    //do something here... 
});

Remarque: target est l'élément sur lequel l'utilisateur a réellement cliqué. Mais le rappel est toujours exécuté dans le contexte de l'élément d'origine, vous pouvez donc utiliser this comme vous le souhaitez dans un rappel jQuery.

Brancher:

$.fn.clickOut = function (parent, fn) {
    var context = this;
    fn = (typeof parent === 'function') ? parent : fn;
    parent = (parent instanceof jQuery) ? parent : $(document);

    context.each(function () {
        var that = this;
        parent.on('click', function (e) {
            var clicked = $(e.target);
            if (!clicked.is(that) && !clicked.parents().is(that)) {
                if (typeof fn === 'function') {
                    fn.call(that, clicked);
                }
            }
        });

    });
    return context;
};

Par défaut, l'écouteur d'événements au clic est placé sur le document. Toutefois, si vous souhaitez limiter la portée de l'écouteur d'événements, vous pouvez transmettre un objet jQuery représentant un élément de niveau parent qui sera le premier parent auquel les clics seront écoutés. Cela évite les écouteurs d'événements de niveau document inutiles. Évidemment, cela ne fonctionnera que si l'élément parent fourni est un parent de votre élément initial. 

Utilisez comme si:

$('div.my-element').clickOut($('div.my-parent'), function(target) { 
    //do something here...
});
5
Matt Goodwin

Je l'ai fait comme ça dans YUI 3:

// Detect the click anywhere other than the overlay element to close it.
Y.one(document).on('click', function (e) {
    if (e.target.ancestor('#overlay') === null && e.target.get('id') != 'show' && overlay.get('visible') == true) {
        overlay.hide();
    }
});

Je vérifie si ancêtre n'est pas le conteneur d'éléments de widget, 
si la cible n’est pas celle qui ouvre le widget/élément, 
Si le widget/élément que je veux fermer est déjà ouvert (pas si important).

5
Satya Prakash

Upvote pour la réponse la plus populaire, mais ajoutez 

&& (e.target != $('html').get(0)) // ignore the scrollbar

un clic sur une barre de défilement ne permet donc pas de masquer ou de masquer l'élément cible.

5
bbe

Cela devrait fonctionner:

$('body').click(function (event) {
    var obj = $(event.target);
    obj = obj['context']; // context : clicked element inside body
    if ($(obj).attr('id') != "menuscontainer" && $('#menuscontainer').is(':visible') == true) {
        //hide menu
    }
});
4
Bilal Berjawi

Une fonction:

$(function() {
    $.fn.click_inout = function(clickin_handler, clickout_handler) {
        var item = this;
        var is_me = false;
        item.click(function(event) {
            clickin_handler(event);
            is_me = true;
        });
        $(document).click(function(event) {
            if (is_me) {
                is_me = false;
            } else {
                clickout_handler(event);
            }
        });
        return this;
    }
});

Usage:

this.input = $('<input>')
    .click_inout(
        function(event) { me.ShowTree(event); },
        function() { me.Hide(); }
    )
    .appendTo(this.node);

Et les fonctions sont très simples:

ShowTree: function(event) {
    this.data_span.show();
}
Hide: function() {
    this.data_span.hide();
}
4
Neco

J'ai utilisé le script ci-dessous et fait avec jQuery.

jQuery(document).click(function(e) {
    var target = e.target; //target div recorded
    if (!jQuery(target).is('#tobehide') ) {
        jQuery(this).fadeOut(); //if the click element is not the above id will hide
    }
})

Ci-dessous, trouvez le code HTML

<div class="main-container">
<div> Hello I am the title</div>
<div class="tobehide">I will hide when you click outside of me</div>
</div>

Vous pouvez lire le tutoriel ici

4
Rinto George

Ceci est ma solution à ce problème:

$(document).ready(function() {
  $('#user-toggle').click(function(e) {
    $('#user-nav').toggle();
    e.stopPropagation();
  });

  $('body').click(function() {
    $('#user-nav').hide(); 
  });

  $('#user-nav').click(function(e){
    e.stopPropagation();
  });
});
4
Spencer Fry

J'ai fini par faire quelque chose comme ça:

$(document).on('click', 'body, #msg_count_results .close',function() {
    $(document).find('#msg_count_results').remove();
});
$(document).on('click','#msg_count_results',function(e) {
    e.preventDefault();
    return false;
});

J'ai un bouton de fermeture dans le nouveau conteneur à des fins d'interface utilisateur conviviale. J'ai dû utiliser return false pour ne pas passer par. Bien sûr, avoir un HREF là-bas pour vous emmener quelque part serait bien, ou vous pourriez appeler des trucs ajax à la place. De toute façon, ça marche pour moi. Juste ce que je voulais.

4
Webmaster G

Nous avons mis en œuvre une solution, basée en partie sur les commentaires d’un utilisateur ci-dessus, qui fonctionne parfaitement pour nous. Nous l'utilisons pour masquer un champ de recherche/les résultats lorsque vous cliquez en dehors de ces éléments, à l'exclusion de l'élément d'origine.

// HIDE SEARCH BOX IF CLICKING OUTSIDE
$(document).click(function(event){ 
    // IF NOT CLICKING THE SEARCH BOX OR ITS CONTENTS OR SEARCH ICON 
    if ($("#search-holder").is(":visible") && !$(event.target).is("#search-holder *, #search")) {
        $("#search-holder").fadeOut('fast');
        $("#search").removeClass('active');
    }
});

Il vérifie également si le champ de recherche est déjà visible en premier, et dans notre cas, il supprime également une classe active sur le bouton de recherche masquer/afficher.

4
Scott Richardson

Les solutions ici fonctionnent bien lorsqu'un seul élément doit être géré. Cependant, s’il existe plusieurs éléments, le problème est beaucoup plus compliqué. Les astuces avec e.stopPropagation () et tous les autres ne fonctionneront pas.

Je suis arrivé avec une solution, et peut-être que ce n'est pas si facile, mais c'est mieux que rien. Regarde:

$view.on("click", function(e) {

    if(model.isActivated()) return;

        var watchUnclick = function() {
            rootView.one("mouseleave", function() {
                $(document).one("click", function() {
                    model.deactivate();
                });
                rootView.one("mouseenter", function() {
                    watchUnclick();
                });
            });
        };
        watchUnclick();
        model.activate();
    });
3
kboom

Voici ce que je fais pour résoudre le problème.

$(window).click(function (event) {
    //To improve performance add a checklike 
    //if(myElement.isClosed) return;
    var isClickedElementChildOfMyBox = isChildOfElement(event,'#id-of-my-element');

    if (isClickedElementChildOfMyBox)
        return;

    //your code to hide the element 
});

var isChildOfElement = function (event, selector) {
    if (event.originalEvent.path) {
        return event.originalEvent.path[0].closest(selector) !== null;
    }

    return event.originalEvent.originalTarget.closest(selector) !== null;
}
3
Fabian

Si quelqu'un de curieux, voici une solution javascript (es6):

window.addEventListener('mouseup', e => {
        if (e.target != yourDiv && e.target.parentNode != yourDiv) {
            yourDiv.classList.remove('show-menu');
            //or yourDiv.style.display = 'none';
        }
    })

et es5, juste au cas où:

window.addEventListener('mouseup', function (e) {
if (e.target != yourDiv && e.target.parentNode != yourDiv) {
    yourDiv.classList.remove('show-menu'); 
    //or yourDiv.style.display = 'none';
}

});

3
Walt

Voici une solution simple en javascript pur. Il est mis à jour avec ES6 :

var isMenuClick = false;
var menu = document.getElementById('menuscontainer');
document.addEventListener('click',()=>{
    if(!isMenuClick){
       //Hide the menu here
    }
    //Reset isMenuClick 
    isMenuClick = false;
})
menu.addEventListener('click',()=>{
    isMenuClick = true;
})
3
Duannx

Ça marche pour moi

$("body").mouseup(function(e) {
    var subject = $(".main-menu");
    if(e.target.id != subject.attr('id') && !subject.has(e.target).length) {
        $('.sub-menu').hide();
    }
});
2
hienbt88
$(document).on("click",function (event)   
 {   
     console.log(event);
   if ($(event.target).closest('.element').length == 0)
     {
    //your code here
      if ($(".element").hasClass("active"))
      {
        $(".element").removeClass("active");
      }
     }
 });

Essayez ce codage pour obtenir la solution.

2
karthikeyan ganesan

Voici mon code:

// Listen to every click
$('html').click(function(event) {
    if ( $('#mypopupmenu').is(':visible') ) {
        if (event.target.id != 'click_this_to_show_mypopupmenu') {
            $('#mypopupmenu').hide();
        }
    }
});

// Listen to selector's clicks
$('#click_this_to_show_mypopupmenu').click(function() {

  // If the menu is visible, and you clicked the selector again we need to hide
  if ( $('#mypopupmenu').is(':visible') {
      $('#mypopupmenu').hide();
      return true;
  }

  // Else we need to show the popup menu
  $('#mypopupmenu').show();
});
2
netdjw

Une autre solution est ici:

http://jsfiddle.net/zR76D/

Usage:

<div onClick="$('#menu').toggle();$('#menu').clickOutside(function() { $(this).hide(); $(this).clickOutside('disable'); });">Open / Close Menu</div>
<div id="menu" style="display: none; border: 1px solid #000000; background: #660000;">I am a menu, whoa is me.</div>

Brancher:

(function($) {
    var clickOutsideElements = [];
    var clickListener = false;

    $.fn.clickOutside = function(options, ignoreFirstClick) {
        var that = this;
        if (ignoreFirstClick == null) ignoreFirstClick = true;

        if (options != "disable") {
            for (var i in clickOutsideElements) {
                if (clickOutsideElements[i].element[0] == $(this)[0]) return this;
            }

            clickOutsideElements.Push({ element : this, clickDetected : ignoreFirstClick, fnc : (typeof(options) != "function") ? function() {} : options });

            $(this).on("click.clickOutside", function(event) {
                for (var i in clickOutsideElements) {
                    if (clickOutsideElements[i].element[0] == $(this)[0]) {
                        clickOutsideElements[i].clickDetected = true;
                    }
                }
            });

            if (!clickListener) {
                if (options != null && typeof(options) == "function") {
                    $('html').click(function() {
                        for (var i in clickOutsideElements) {
                            if (!clickOutsideElements[i].clickDetected) {
                                clickOutsideElements[i].fnc.call(that);
                            }
                            if (clickOutsideElements[i] != null) clickOutsideElements[i].clickDetected = false;
                        }
                    });
                    clickListener = true;
                }
            }
        }
        else {
            $(this).off("click.clickoutside");
            for (var i = 0; i < clickOutsideElements.length; ++i) {
                if (clickOutsideElements[i].element[0] == $(this)[0]) {
                    clickOutsideElements.splice(i, 1);
                }
            }
        }

        return this;
    }
})(jQuery);
2
teynon

Si vous utilisez des outils tels que "Pop-up", vous pouvez utiliser l'événement "onFocusOut".

window.onload=function(){
document.getElementById("inside-div").focus();
}
function loseFocus(){
alert("Clicked outside");
}
#container{
background-color:lightblue;
width:200px;
height:200px;
}

#inside-div{
background-color:lightgray;
width:100px;
height:100px;

}
<div id="container">
<input type="text" id="inside-div" onfocusout="loseFocus()">
</div>

2
Muhammet Can TONBUL

Je veux juste que la réponse de @Pistos soit plus claire car elle est cachée dans les commentaires.

Cette solution a parfaitement fonctionné pour moi. JS simple:

var elementToToggle = $('.some-element');
$(document).click( function(event) {
  if( $(event.target).closest(elementToToggle).length === 0 ) {
    elementToToggle.hide();
  }
});

dans CoffeeScript:

elementToToggle = $('.some-element')
$(document).click (event) ->
  if $(event.target).closest(elementToToggle).length == 0
    elementToToggle.hide()
2
DaniG2k

Cela a parfaitement fonctionné à temps pour moi:

$('body').click(function() {
    // Hide the menus if visible.
});
2
govind

Je sais qu'il y a un million de réponses à cette question, mais j'ai toujours été fan de HTML et de CSS pour effectuer la majeure partie du travail. Dans ce cas, z-index et positionnement. La manière la plus simple que j'ai trouvée de faire ceci est la suivante:

$("#show-trigger").click(function(){
  $("#element").animate({width: 'toggle'});
  $("#outside-element").show();
});
$("#outside-element").click(function(){
  $("#element").hide();
  $("#outside-element").hide();
});
#outside-element {
  position:fixed;
  width:100%;
  height:100%;
  z-index:1;
  display:none;
}
#element {
  display:none;
  padding:20px;
  background-color:#ccc;
  width:300px;
  z-index:2;
  position:relative;
}
#show-trigger {
  padding:20px;
  background-color:#ccc;
  margin:20px auto;
  z-index:2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="outside-element"></div>
<div id="element">
  <div class="menu-item"><a href="#1">Menu Item 1</a></div>
  <div class="menu-item"><a href="#2">Menu Item 1</a></div>
  <div class="menu-item"><a href="#3">Menu Item 1</a></div>
  <div class="menu-item"><a href="#4">Menu Item 1</a></div>
</div>
<div id="show-trigger">Show Menu</div>

Cela crée un environnement sûr, car rien ne sera déclenché à moins que le menu ne soit réellement ouvert et que l'index z empêche tout contenu de l'élément de créer des ratés lors du clic.

De plus, vous n'avez pas besoin de jQuery pour couvrir toutes vos bases avec des appels de propagation et pour purger tous les éléments internes des ratés.

2
Lucas

La réponse marquée comme réponse acceptée ne prend pas en compte le fait que vous pouvez avoir des superpositions sur l'élément, telles que des boîtes de dialogue, des popovers, des sélecteurs de date, etc.

J'ai créé ma propre version qui en tient compte. Il est créé en tant que KnockoutJS binding, mais il peut facilement être converti en jQuery uniquement.

Cela fonctionne à partir de la première requête pour tous les éléments visibles avec une position absolue ou une position absolue. Il frappe ensuite les éléments en question par rapport à ceux que je souhaite masquer si vous cliquez à l'extérieur. Si c'est un succès, je calcule un nouveau rectangle lié qui prend en compte les limites de superposition.

ko.bindingHandlers.clickedIn = (function () {
    function getBounds(element) {
        var pos = element.offset();
        return {
            x: pos.left,
            x2: pos.left + element.outerWidth(),
            y: pos.top,
            y2: pos.top + element.outerHeight()
        };
    }

    function hitTest(o, l) {
        function getOffset(o) {
            for (var r = { l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth, b: o.offsetHeight };
                o = o.offsetParent; r.l += o.offsetLeft, r.t += o.offsetTop);
            return r.r += r.l, r.b += r.t, r;
        }

        for (var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j ? l = [l] : l).length; i;
            b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r : b.l <= a.r))
                && (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) && (r[r.length] = l[i]));
        return j ? !!r.length : r;
    }

    return {
        init: function (element, valueAccessor) {
            var target = valueAccessor();
            $(document).click(function (e) {
                if (element._clickedInElementShowing === false && target()) {
                    var $element = $(element);
                    var bounds = getBounds($element);

                    var possibleOverlays = $("[style*=z-index],[style*=absolute]").not(":hidden");
                    $.each(possibleOverlays, function () {
                        if (hitTest(element, this)) {
                            var b = getBounds($(this));
                            bounds.x = Math.min(bounds.x, b.x);
                            bounds.x2 = Math.max(bounds.x2, b.x2);
                            bounds.y = Math.min(bounds.y, b.y);
                            bounds.y2 = Math.max(bounds.y2, b.y2);
                        }
                    });

                    if (e.clientX < bounds.x || e.clientX > bounds.x2 ||
                        e.clientY < bounds.y || e.clientY > bounds.y2) {

                        target(false);
                    }
                }
                element._clickedInElementShowing = false;
            });

            $(element).click(function (e) {
                e.stopPropagation();
            });
        },
        update: function (element, valueAccessor) {
            var showing = ko.utils.unwrapObservable(valueAccessor());
            if (showing) {
                element._clickedInElementShowing = true;
            }
        }
    };
})();
2
Anders

Pour être honnête, je n’ai aimé aucune des solutions précédentes.

La meilleure façon de le faire est de lier l'événement "click" au document et de comparer si ce clic est vraiment en dehors de l'élément (tout comme Art l'a dit dans sa suggestion).

Cependant, vous rencontrerez quelques problèmes: vous ne pourrez jamais le dissocier et vous ne pouvez pas avoir de bouton externe pour ouvrir/fermer cet élément.

C'est pourquoi j'ai écrit ce petit plugin (cliquez ici pour le lien) , pour simplifier ces tâches. Pourrait-il être plus simple?

<a id='theButton' href="#">Toggle the menu</a><br/>
<div id='theMenu'>
    I should be toggled when the above menu is clicked,
    and hidden when user clicks outside.
</div>

<script>
$('#theButton').click(function(){
    $('#theMenu').slideDown();
});
$("#theMenu").dClickOutside({ ignoreList: $("#theButton") }, function(clickedObj){
    $(this).slideUp();
});
</script>
2
Alexandre T.

Pour ce faire, la méthode la plus large consiste à tout sélectionner sur la page Web, à l'exception de l'élément pour lequel vous ne souhaitez pas que les clics soient détectés, et à lier l'événement click à l'ouverture du menu. 

Ensuite, lorsque le menu est fermé, supprimez la liaison.

Utilisez .stopPropagation pour éviter que l'événement n'affecte une partie quelconque du menuscontainer.

$("*").not($("#menuscontainer")).bind("click.OutsideMenus", function ()
{
    // hide the menus

    //then remove all of the handlers
    $("*").unbind(".OutsideMenus");
});

$("#menuscontainer").bind("click.OutsideMenus", function (event) 
{
    event.stopPropagation(); 
});
2
maday
jQuery().ready(function(){
    $('#nav').click(function (event) {
        $(this).addClass('activ');
        event.stopPropagation();
    });

    $('html').click(function () {
        if( $('#nav').hasClass('activ') ){
            $('#nav').removeClass('activ');
        }
    });
});
2
Toogy

Pour les appareils tactiles comme iPad et iPhone, nous pouvons utiliser ce code:

$(document).on('touchstart', function (event) {
    var container = $("YOUR CONTAINER SELECTOR");

    if (!container.is(e.target) &&            // If the target of the click isn't the container...
        container.has(e.target).length === 0) // ... nor a descendant of the container
    {
        container.hide();
    }
});
2

Cela pourrait être une meilleure solution pour certaines personnes. 

$(".menu_link").click(function(){
    // show menu code
});

$(".menu_link").mouseleave(function(){
    //hide menu code, you may add a timer for 3 seconds before code to be run
});

Je sais que mouseleave ne signifie pas seulement un clic à l'extérieur, mais aussi que vous quittez la zone de cet élément.

Une fois que le menu lui-même est à l'intérieur de l'élément menu_link, le menu lui-même ne devrait pas poser de problème pour cliquer ou pour continuer.

1
Waheed
$('#propertyType').on("click",function(e){
          self.propertyTypeDialog = !self.propertyTypeDialog;
          b = true;
          e.stopPropagation();
          console.log("input clicked");
      });

      $(document).on('click','body:not(#propertyType)',function (e) {
          e.stopPropagation();
          if(b == true)  {
              if ($(e.target).closest("#configuration").length == 0) {
                  b = false;
                  self.propertyTypeDialog = false;
                  console.log("outside clicked");
              }
          }
        // console.log($(e.target).closest("#configuration").length);
      });
1
chea sotheara

Je crois que la meilleure façon de le faire est quelque chose comme ça. 

$(document).on("click", function(event) {
  clickedtarget = $(event.target).closest('#menuscontainer');
  $("#menuscontainer").not(clickedtarget).hide();
});

Ce type de solution pourrait facilement être utilisé pour plusieurs menus, ainsi que pour les menus ajoutés dynamiquement via javascript. En gros, il vous permet simplement de cliquer n'importe où dans votre document, de vérifier l'élément sur lequel vous avez cliqué et de sélectionner son "#menuscontainer" le plus proche. Ensuite, tous les ménusconteneurs sont masqués, à l’exclusion de celui sur lequel vous avez cliqué. 

Vous ne savez pas exactement comment vos menus sont construits, mais n'hésitez pas à copier mon code dans le JSFiddle. C'est un système menu/modal très simple mais parfaitement fonctionnel. Tout ce que vous avez à faire est de construire les menus HTML et le code fera le travail pour vous.

https://jsfiddle.net/zs6anrn7/

1
Waltur Buerk

Pour masquer fileTreeClass si l'utilisateur clique dessus 

 jQuery(document).mouseup(function (e) {
            var container = $(".fileTreeClass");
            if (!container.is(e.target) // if the target of the click isn't the container...
                && container.has(e.target).length === 0) // ... nor a descendant of the container
            {
                container.hide();
            }
        });
1
Thamaraiselvam

Plugin simple:

$.fn.clickOff = function(callback, selfDestroy) {
    var clicked = false;
    var parent = this;
    var destroy = selfDestroy || true;

    parent.click(function() {
        clicked = true;
    });

    $(document).click(function(event) {
        if (!clicked && parent.is(':visible')) {
            if(callback) callback.call(parent, event)
        }
        if (destroy) {
            //parent.clickOff = function() {};
            //parent.off("click");
            //$(document).off("click");
            parent.off("clickOff");
        }
        clicked = false;
    });
};

Utilisation:

$("#myDiv").clickOff(function() {
   alert('clickOff');
});
1
froilanq

si vous souhaitez simplement afficher une fenêtre lorsque vous cliquez sur un bouton et que cette fenêtre est décompressée lorsque vous cliquez à l'extérieur (ou à nouveau sur le bouton), cela fonctionne bien.

document.body.onclick = function() { undisp_menu(); };
var menu_on = 0;

function menu_trigger(event){

    if (menu_on == 0)
    {
        // otherwise u will call the undisp on body when 
        // click on the button
        event.stopPropagation(); 

        disp_menu();
    }

    else{
        undisp_menu();
    }

}


function disp_menu(){

    menu_on = 1;
    var e = document.getElementsByClassName("menu")[0];
    e.className = "menu on";

}

function undisp_menu(){

    menu_on = 0;
    var e = document.getElementsByClassName("menu")[0];
    e.className = "menu";

}

ne l'oubliez pas pour le bouton

<div class="button" onclick="menu_trigger(event)">

<div class="menu">

et le css:

.menu{
    display: none;
}

.on {
    display: inline-block;
}
0
Aominé

Essayez ceci:

$('html').click(function(e) {
  if($(e.target).parents('#menuscontainer').length == 0) {
    $('#menuscontainer').hide();
  }
});

https://jsfiddle.net/4cj4jxy0/

Notez cependant que cela ne peut pas fonctionner si l'événement click ne peut pas atteindre la balise html. (Peut-être que d'autres éléments ont stopPropagation()).

0
Yishu Fang

Juste un avertissement qui utilise ceci:

$('html').click(function() {
  // Hide the menus if visible
});

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

Il empêche le pilote Ruby on Rails UJS de fonctionner correctement. Par exemple, link_to 'click', '/url', :method => :delete ne fonctionnera pas.

Cela pourrait être une solution de contournement:

$('html').click(function() {
  // Hide the menus if visible
});

$('#menucontainer').click(function(event){
  if (!$(event.target).data('method')) {
    event.stopPropagation();
  }
});
0
mlangenberg

$('html').click(function() {
//Hide the menus if visible
});

$('#menucontainer').click(function(event){
    event.stopPropagation();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
 <button id='#menucontainer'>Ok</button> 
</html>

0
Autumnswind

Souscrire phase de capture de clic pour gérer cliquez sur les éléments qui appellent preventDefault.
Recouvrez-le sur l'élément du document en utilisant l'autre nom click-anywhere.

document.addEventListener('click', function (event) {
  event = $.event.fix(event);
  event.type = 'click-anywhere';
  $document.trigger(event);
}, true);

Ensuite, là où vous avez besoin de fonctionnalités extérieures au clic, abonnez-vous à l'événement click-anywhere sur document et vérifiez si le clic s'est déroulé en dehors de l'élément qui vous intéresse:

$(document).on('click-anywhere', function (event) {
  if (!$(event.target).closest('#smth').length) {
    // Do anything you need here
  }
});

Quelques notes:

  • Vous devez utiliser document, car ce serait une faute de performance de déclencher un événement sur tous les éléments en dehors desquels le clic est survenu.

  • Cette fonctionnalité peut être encapsulée dans un plugin spécial, qui appelle un rappel sur un clic extérieur.

  • Vous ne pouvez pas vous abonner à la phase de capture en utilisant jQuery lui-même.

  • Vous n'avez pas besoin du chargement de document pour vous abonner car l'abonnement est sur document, même pas sur sa body, de sorte qu'il existe toujours indépendamment de l'emplacement du script et de l'état du chargement.

0
Qwertiy

Supposons que la div que vous voulez détecter si l'utilisateur qui a cliqué à l'extérieur ou à l'intérieur a un identifiant, par exemple: "my-special-widget".

Écoutez les événements de clic corporel:

document.body.addEventListener('click', (e) => {
    if (isInsideMySpecialWidget(e.target, "my-special-widget")) {
        console.log("user clicked INSIDE the widget");
    }
    console.log("user clicked OUTSIDE the widget");
});

function isInsideMySpecialWidget(elem, mySpecialWidgetId){
    while (elem.parentElement) {
        if (elem.id === mySpecialWidgetId) {
            return true;
        }
        elem = elem.parentElement;
    }
    return false;
}

Dans ce cas, vous n'interrompez pas le flux normal de clic sur un élément de votre page, car vous n'utilisez pas la méthode "stopPropagation".

0
Yair Cohen
$(document).on('click.menu.hide', function(e){
  if ( !$(e.target).closest('#my_menu').length ) {
    $('#my_menu').find('ul').toggleClass('active', false);
  }
});

$(document).on('click.menu.show', '#my_menu li', function(e){
  $(this).find('ul').toggleClass('active');
});
div {
  float: left;
}

ul {
  padding: 0;
  position: relative;
}
ul li {
  padding: 5px 25px 5px 10px;
  border: 1px solid silver;
  cursor: pointer;
  list-style: none;
  margin-top: -1px;
  white-space: nowrap;
}
ul li ul:before {
  margin-right: -20px;
  position: absolute;
  top: -17px;
  right: 0;
  content: "\25BC";
}
ul li ul li {
  visibility: hidden;
  height: 0;
  padding-top: 0;
  padding-bottom: 0;
  border-width: 0 0 1px 0;
}
ul li ul li:last-child {
  border: none;
}
ul li ul.active:before {
  content: "\25B2";
}
ul li ul.active li {
  display: list-item;
  visibility: visible;
  height: inherit;
  padding: 5px 25px 5px 10px;
}
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<div>
  <ul id="my_menu">
    <li>Menu 1
      <ul>
        <li>subMenu 1</li>
        <li>subMenu 2</li>
        <li>subMenu 3</li>
        <li>subMenu 4</li>
      </ul>
    </li>
    <li>Menu 2
      <ul>
        <li>subMenu 1</li>
        <li>subMenu 2</li>
        <li>subMenu 3</li>
        <li>subMenu 4</li>
      </ul>
    </li>
    <li>Menu 3</li>
    <li>Menu 4</li>
    <li>Menu 5</li>
    <li>Menu 6</li>
  </ul>
</div>

Voici la version de jsbin http://jsbin.com/xopacadeni/edit?html,css,js,output

0
FDisk
    $('#menucontainer').click(function(e){
        e.stopPropagation();
     });

    $(document).on('click',  function(e){
        // code
    });
0
Nitekurs

Cela fera basculer le menu de navigation lorsque vous cliquez sur l’élément. 

$(document).on('click', function(e) {
    var elem = $(e.target).closest('#menu'),
    box = $(e.target).closest('#nav');
 if (elem.length) {
    e.preventDefault();
    $('#nav').toggle();
  } else if (!box.length) {
    $('#nav').hide();
 }
});



<li id="menu"><a></a></li>
<ul id="nav" >  //Nav will toggle when you Click on Menu(it can be an icon in this example)
        <li class="page"><a>Page1</a></li>
        <li class="page"><a>Pag2</a></li>
        <li class="page"><a>Page3</a></li>            
        <li class="page"><a>Page4</a></li>
</ul>
0
Awena

Il s'agit d'une solution plus générale qui permet de surveiller plusieurs éléments et d'ajouter et de supprimer dynamiquement des éléments de la file d'attente.

Il contient une file d'attente globale (autoCloseQueue) - un conteneur d'objets pour les éléments devant être fermés en dehors des clics. 

Chaque clé d'objet de file d'attente doit être l'ID d'élément DOM et la valeur doit être un objet avec 2 fonctions de rappel: 

 {onPress: someCallbackFunction, onOutsidePress: anotherCallbackFunction}

Mettez ceci dans votre rappel de document prêt:

window.autoCloseQueue = {}  

$(document).click(function(event) {
    for (id in autoCloseQueue){
        var element = autoCloseQueue[id];
        if ( ($(e.target).parents('#' + id).length) > 0) { // This is a click on the element (or its child element)
            console.log('This is a click on an element (or its child element) with  id: ' + id);
            if (typeof element.onPress == 'function') element.onPress(event, id);
        } else { //This is a click outside the element
            console.log('This is a click outside the element with id: ' + id);
            if (typeof element.onOutsidePress == 'function') element.onOutsidePress(event, id); //call the outside callback
            delete autoCloseQueue[id]; //remove the element from the queue
        }
    }
});

Ensuite, lorsque l'élément DOM portant l'id 'menuscontainer' est créé, ajoutez simplement cet objet à la file d'attente: 

window.autoCloseQueue['menuscontainer'] = {onOutsidePress: clickOutsideThisElement}
0
Roei Bahumi