Éviter menu déroulant fermer sur Cliquez à l'intérieur
J'ai un menu déroulant Twitter Bootstrap. Comme tous les utilisateurs de Twitter Bootstrap le savent, le menu déroulant se ferme sur le clic (même en cliquant à l'intérieur).
Pour éviter cela, je peux facilement attacher un gestionnaire d'événement click dans le menu déroulant et simplement ajouter le fameux event.stopPropagation()
.
<ul class="nav navbar-nav">
<li class="dropdown mega-dropdown">
<a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-list-alt"></i> Menu item 1
<span class="fa fa-chevron-down pull-right"></span>
</a>
<ul class="dropdown-menu mega-dropdown-menu">
<li>
<div id="carousel" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
<li data-slide-to="0" data-target="#carousel"></li>
<li class="active" data-slide-to="1" data-target="#carousel"></li>
</ol>
<div class="carousel-inner">
<div class="item">
<img alt="" class="img-rounded" src="img1.jpg">
</div>
<div class="item active">
<img alt="" class="img-rounded" src="img2.jpg">
</div>
</div>
<a data-slide="prev" role="button" href="#carousel"
class="left carousel-control">
<span class="glyphicon glyphicon-chevron-left"></span>
</a>
<a data-slide="next" role="button" href="#carousel"
class="right carousel-control">
<span class="glyphicon glyphicon-chevron-right"></span>
</a>
</div>
</li>
</ul>
</li>
</ul>
Cela semble facile et un comportement très commun, cependant, et puisque carousel-controls
(ainsi que carousel indicators
) les gestionnaires d'événements sont délégués à l'objet document
, l'événement click
sur ces éléments (précédent / suivant contrôle, ...) sera "ignoré".
$('ul.dropdown-menu.mega-dropdown-menu').on('click', function(event){
// The event won't be propagated up to the document NODE and
// therefore delegated events won't be fired
event.stopPropagation();
});
S'Appuyant sur Twitter Bootstrap déroulante hide
/hidden
les événements n'est pas une solution pour les raisons suivantes:
- l'événement fourni pour les deux gestionnaires d'événements ne vous donne pas de référence à l'élément cliqué
- le contenu du menu déroulant est généré dynamiquement, donc l'ajout d'une classe
flag
n'est pas possible
Ce violon est le comportement normal et ce violon est avec event.stopPropagation()
ajouter.
Mise à jour
Merci à Romain pour sa réponse. J'ai également trouvé une réponse que vous pouvez trouver ci-dessous.
24 réponses
Supprimer l'attribut de données data-toggle="dropdown"
et implémenter l'ouverture / fermeture de la liste déroulante peut être une solution.
D'abord en manipulant le clic sur le lien pour ouvrir / fermer la liste déroulante comme ceci:
$('li.dropdown.mega-dropdown a').on('click', function (event) {
$(this).parent().toggleClass('open');
});
Et puis en écoutant les clics en dehors de la liste déroulante pour le fermer comme ceci:
$('body').on('click', function (e) {
if (!$('li.dropdown.mega-dropdown').is(e.target)
&& $('li.dropdown.mega-dropdown').has(e.target).length === 0
&& $('.open').has(e.target).length === 0
) {
$('li.dropdown.mega-dropdown').removeClass('open');
}
});
Voici la démo : http://jsfiddle.net/RomaLefrancois/hh81rhcm/2/
Cela devrait aussi aider
$(document).on('click', 'someyourContainer .dropdown-menu', function (e) {
e.stopPropagation();
});
Bootstrap donne la fonction suivante:
| This event is fired immediately when the hide instance method hide.bs.dropdown | has been called. The toggling anchor element is available as the | relatedTarget property of the event.
Par conséquent, l'implémentation de cette fonction devrait pouvoir désactiver la liste déroulante de la fermeture.
$('#myDropdown').on('hide.bs.dropdown', function (e) {
var target = $(e.target);
if(target.hasClass("keepopen") || target.parents(".keepopen").length){
return false; // returning false should stop the dropdown from hiding.
}else{
return true;
}
});
La meilleure réponse absolue est de mettre une balise de formulaire après le menu déroulant de classe
Donc votre code est
<ul class="dropdown-menu">
<form>
<li>
<div class="menu-item">bla bla bla</div>
</li>
</form>
</ul>
J'ai aussi trouvé une solution.
En supposant que les gestionnaires d'événements liés à Twitter Bootstrap Components
sont délégués à l'objet document
, je boucle les gestionnaires attachés et vérifie si l'élément cliqué en cours (ou l'un de ses parents) est concerné par un événement délégué.
$('ul.dropdown-menu.mega-dropdown-menu').on('click', function(event){
var events = $._data(document, 'events') || {};
events = events.click || [];
for(var i = 0; i < events.length; i++) {
if(events[i].selector) {
//Check if the clicked element matches the event selector
if($(event.target).is(events[i].selector)) {
events[i].handler.call(event.target, event);
}
// Check if any of the clicked element parents matches the
// delegated event selector (Emulating propagation)
$(event.target).parents(events[i].selector).each(function(){
events[i].handler.call(this, event);
});
}
}
event.stopPropagation(); //Always stop propagation
});
J'espère que cela aidera quelqu'un à la recherche d'une solution similaire.
Merci à tous pour votre aide.
$('body').on("click", ".dropdown-menu", function (e) {
$(this).parent().is(".open") && e.stopPropagation();
});
Cela peut fonctionner pour toutes les conditions.
Cela pourrait aider:
$("dropdownmenuname").click(function(e){
e.stopPropagation();
})
JQuery:
<script>
$(document).on('click.bs.dropdown.data-api', '.dropdown.keep-inside-clicks-open', function (e) {
e.stopPropagation();
});
</script>
HTML:
<div class="dropdown keep-inside-clicks-open">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
Dropdown Example
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#">HTML</a></li>
<li><a href="#">CSS</a></li>
<li><a href="#">JavaScript</a></li>
</ul>
</div>
Démo:
Générique: https://jsfiddle.net/kerryjohnson/omefq68b/1/
Votre démo avec cette solution: http://jsfiddle.net/kerryjohnson/80oLdtbf/101/
$('ul.nav.navbar-nav').on('click.bs.dropdown', function(e){
var $a = $(e.target), is_a = $a.is('.is_a');
if($a.hasClass('dropdown-toggle')){
$('ul.dropdown-menu', this).toggle(!is_a);
$a.toggleClass('is_a', !is_a);
}
}).on('mouseleave', function(){
$('ul.dropdown-menu',this).hide();
$('.is_a', this).removeClass('is_a');
});
Je l'ai mis à jour une fois de plus pour être le plus intelligent et fonctionnel possible. il se ferme maintenant lorsque vous survolez à l'extérieur du nav, restant ouvert pendant que vous êtes à l'intérieur. tout simplement parfait.
J'ai récemment eu un problème similaire et j'ai essayé différentes façons de le résoudre en supprimant l'attribut de données data-toggle="dropdown"
et en écoutant click
avec event.stopPropagation()
appelant.
La deuxième façon semble plus préférable. Aussi les développeurs Bootstrap utilisent de cette façon. Dans le fichier source, j'ai trouvé l'initialisation des éléments déroulants:
// APPLY TO STANDARD DROPDOWN ELEMENTS
$(document)
.on('click.bs.dropdown.data-api', clearMenus)
.on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
.on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
.on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
.on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
}(jQuery);
Donc, cette ligne:
.on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
Suggère que vous pouvez mettre un élément form
dans le conteneur avec la classe .dropdown
pour éviter de fermer le menu déroulant.
Vous pouvez arrêter de cliquer sur la liste déroulante de la propagation, puis réimplémenter manuellement les contrôles carrousel en utilisant méthodes JavaScript carrousel .
$('ul.dropdown-menu.mega-dropdown-menu').on('click', function(event) {
event.stopPropagation();
});
$('a.left').click(function () {
$('#carousel').carousel('prev');
});
$('a.right').click(function () {
$('#carousel').carousel('next');
});
$('ol.carousel-indicators li').click(function (event) {
var index = $(this).data("slide-to");
$('#carousel').carousel(index);
});
Est Ici le jsfiddle.
Avec Angular2 Bootstrap, vous pouvez utiliser nonInput pour la plupart des scénarios:
<div dropdown autoClose="nonInput">
NonInput - (par défaut) ferme automatiquement la liste déroulante lorsque l'un de ses éléments est cliqué-tant que l'élément cliqué n'est pas une entrée ou une zone de texte.
Comme par exemple Bootstrap 4 Alpha a cet événement de Menu. Pourquoi ne pas l'utiliser?
// PREVENT INSIDE MEGA DROPDOWN
$('.dropdown-menu').on("click.bs.dropdown", function (e) {
e.stopPropagation();
e.preventDefault();
});
Je sais que cette question était spécifiquement pour jQuery, mais pour toute personne utilisant AngularJS qui a ce problème, vous pouvez créer une directive qui gère ceci:
angular.module('app').directive('dropdownPreventClose', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.on('click', function(e) {
e.stopPropagation(); //prevent the default behavior of closing the dropdown-menu
});
}
};
});
Ensuite, ajoutez simplement l'attribut dropdown-prevent-close
à votre élément qui déclenche la fermeture du menu, et cela devrait l'empêcher. Pour moi, c'était un élément select
qui fermait automatiquement le menu:
<div class="dropdown-menu">
<select dropdown-prevent-close name="myInput" id="myInput" ng-model="myModel">
<option value="">Select Me</option>
</select>
</div>
[Bootstrap 4 Alpha 6] [Rails]
Pour le développeur rails, e.stopPropagation()
conduira à un comportement indésirable pour link_to
avec data-method
pas égal à get
car il retournera par défaut toute votre requête comme get
.
Pour remédier à ce problème, je suggère cette solution, qui est universelle
$('.dropdown .dropdown-menu').on('click.bs.dropdown', function() {
return $('.dropdown').one('hide.bs.dropdown', function() {
return false;
});
});
$('.dropdown .dropdown-menu').on('click.bs.dropdown', function() {
return $('.dropdown').one('hide.bs.dropdown', function() {
return false;
});
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
<ul class="nav navbar-nav">
<li class="dropdown mega-dropdown">
<a href="javascript:;" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-list-alt"></i> Menu item 1
<span class="fa fa-chevron-down pull-right"></span>
</a>
<ul class="dropdown-menu mega-dropdown-menu">
<li>
<div id="carousel" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
<li data-slide-to="0" data-target="#carousel"></li>
<li class="active" data-slide-to="1" data-target="#carousel"></li>
</ol>
<div class="carousel-inner">
<div class="item">
<img alt="" class="img-rounded" src="img1.jpg">
</div>
<div class="item active">
<img alt="" class="img-rounded" src="img2.jpg">
</div>
</div>
<a data-slide="prev" role="button" href="#carousel" class="left carousel-control">
<span class="glyphicon glyphicon-chevron-left"></span>
</a>
<a data-slide="next" role="button" href="#carousel" class="right carousel-control">
<span class="glyphicon glyphicon-chevron-right"></span>
</a>
</div>
</li>
</ul>
</li>
</ul>
La solution de travail la plus simple pour moi est:
- ajout de la classe
keep-open
aux éléments qui ne devraient pas provoquer la fermeture de la liste déroulante - et ce morceau de code fait le reste:
$('.dropdown').on('click', function(e) {
var target = $(e.target);
var dropdown = target.closest('.dropdown');
return !dropdown.hasClass('open') || !target.hasClass('keep-open');
});
J'ai trouvé qu'aucune des solutions ne fonctionnait comme je voudrais utiliser le nav Bootstrap par défaut. Voici ma solution à ce problème:
$(document).on('hide.bs.dropdown', function (e) {
if ($(e.currentTarget.activeElement).hasClass('dropdown-toggle')) {
$(e.relatedTarget).parent().removeClass('open');
return true;
}
return false;
});
Au lieu d'écrire du code javascript ou jquery(réinventer la roue). Le scénario ci-dessus peut être géré par l'option de fermeture automatique bootstrap. Vous pouvez fournir l'une des valeurs à Auto-close :
Toujours - (par défaut) ferme automatiquement la liste déroulante lorsque l'un de ses éléments est cliqué.
OutsideClick-ferme automatiquement la liste déroulante uniquement lorsque l'utilisateur clique sur un élément en dehors de la liste déroulante.
Désactivé-désactive l'auto fermer
Jetez un oeil à ce qui suit plunkr:
Http://plnkr.co/edit/gnU8M2fqlE0GscUQtCWa?p=preview
Ensemble
uib-dropdown auto-close="disabled"
J'espère que cela aide :)
Dans .dropdown
content mettez la classe .keep-open
sur n'importe quelle étiquette comme ceci:
$('.dropdown').on('click', function (e) {
var target = $(e.target);
var dropdown = target.closest('.dropdown');
if (target.hasClass('keep-open')) {
$(dropdown).addClass('keep-open');
} else {
$(dropdown).removeClass('keep-open');
}
});
$(document).on('hide.bs.dropdown', function (e) {
var target = $(e.target);
if ($(target).is('.keep-open')) {
return false
}
});
Les cas précédents évitaient les événements liés aux objets conteneur, maintenant le conteneur hérite de la classe keep-open Et check avant d'être fermé.
Je sais qu'il y a déjà une réponse précédente suggérant d'utiliser un formulaire mais le balisage fourni n'est pas correct / idéal. Voici la solution la plus simple, pas besoin de javascript et cela ne casse pas votre liste déroulante. Fonctionne avec Bootstrap 4.
<form class="dropdown-item">
<!-- Your elements go here -->
</form>
$('body').on("click", ".dropdown-menu", function (e) {
$(this).parent().is(".show") && e.stopPropagation();
});
Cela fonctionne en 2018
$(function() {
$('.mega-dropdown').on('hide.bs.dropdown', function(e) {
var $target = $(e.target);
return !($target.hasClass("keep-open") || $target.parents(".keep-open").size() > 0);
});
$('.mega-dropdown > ul.dropdown-menu').on('mouseenter', function() {
$(this).parent('li').addClass('keep-open')
}).on('mouseleave', function() {
$(this).parent('li').removeClass('keep-open')
});
});
Pour fermer la liste déroulante uniquement si un événement de clic a été déclenché en dehors de la liste déroulante bootstrap, c'est ce qui a fonctionné pour moi:
Fichier JS:
$('.createNewElement').on('click.bs.dropdown.data-api', '.tags-btn-group.keep-open-dropdown', function (e) {
var target = $(e.target);
if (target.hasClass("dropdown-menu") || target.parents(".dropdown-menu").length) {
e.stopPropagation();
}
});
Fichier HTML:
<!-- button: -->
<div class="createNewElement">
<div class="btn-group tags-btn-group keep-open-dropdown">
<div class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">OPEN DROPDOWN</div>
<ul class="dropdown-menu">
WHAT EVER YOU WANT HERE...
</ul>
</div>
</div>
$(function() {
var closeble = false;
$('body').on('click', function (e) {
if (!$(event.target).is("a.dropdown-toggle")) {
closeble = false;
}
});
$('.dropdown').on({
"click": function(event) {
if ($(event.target).closest('.dropdown-toggle').length) {
closeble = true;
} else {
closeble = false;
}
},
"hide.bs.dropdown": function() {
return closeble;
}
});
});