web-dev-qa-db-fra.com

Éviter le menu déroulant fermer en cliquant sur l'intérieur

J'ai un menu déroulant Twitter Bootstrap. Comme le savent tous les utilisateurs de Twitter Bootstrap, le menu déroulant se ferme au clic (même en cliquant à l'intérieur).

Pour éviter cela, je peux facilement attacher un gestionnaire d’événements click dans le menu déroulant et ajouter simplement 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 très courant, cependant, et puisque les gestionnaires d'événements carousel-controls (ainsi que carousel indicators) sont délégués à l'objet document, l'événement click sur ces éléments (contrôles prev/next, ...) Etre 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 les listes déroulantes Bootstrap de Twitter hide/hidden 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. Il est donc impossible d'ajouter une classe flag

Ce violon est le comportement normal et ce violon est avec event.stopPropagation() ajouté.

Mettre à jour

Merci à Roman pour sa réponse . J'ai aussi trouvé un réponse que vous pouvez trouver ci-dessous .

239
php-dev

Supprimer l'attribut de données data-toggle="dropdown" et implémenter l'ouverture/la fermeture du menu déroulant peut constituer une solution.

Commencez par cliquer sur le lien pour ouvrir/fermer le menu déroulant comme suit:

$('li.dropdown.mega-dropdown a').on('click', function (event) {
    $(this).parent().toggleClass('open');
});

puis écoutez les clics en dehors du menu déroulant pour le fermer comme suit:

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

323
Roma

Cela devrait aider aussi

$(document).on('click', 'someyourContainer .dropdown-menu', function (e) {
  e.stopPropagation();
});
250
Arbejdsglæde

Bootstrap fournit la fonction suivante:

                  | Cet événement est déclenché immédiatement lorsque la méthode d'occurrence de masquage 
 Hide.bs.dropdown | a été appelé. L'élément d'ancrage à bascule est disponible en tant que 
 | relatedTarget propriété de l'événement.

Par conséquent, l'implémentation de cette fonction devrait pouvoir désactiver la fermeture du menu déroulant.

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

La meilleure solution consiste à placer une balise de formulaire après le menu déroulant de la classe.

alors votre code est

<ul class="dropdown-menu">
  <form>
    <li>
      <div class="menu-item">bla bla bla</div>
    </li>
  </form>
</ul>
26
user3287193

J'ai aussi trouvé une solution.

En supposant que les gestionnaires d'événements liés à Twitter Bootstrap Components soient délégués à l'objet document, je boucle les gestionnaires attachés et vérifie si l'élément sur lequel l'utilisateur a cliqué (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 tout le monde à la recherche d'une solution similaire.

Merci à tous pour votre aide.

25
php-dev
$('body').on("click", ".dropdown-menu", function (e) {
    $(this).parent().is(".open") && e.stopPropagation();
});

Cela peut fonctionner pour toutes les conditions.

19
Terry Lin

Cela pourrait aider: 

$("dropdownmenuname").click(function(e){
   e.stopPropagation();
})
15
Bawantha

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/

9
Kerry Johnson

J'ai récemment eu un problème similaire et j'ai essayé différentes manières de le résoudre en supprimant l'attribut de données data-toggle="dropdown" et en écoutant click avec event.stopPropagation() en appelant.

La deuxième façon semble plus préférable. Les développeurs Bootstrap utilisent également cette méthode . 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 de placer un élément form dans le conteneur avec la classe .dropdown afin d'éviter de fermer le menu déroulant.

$('body').on("click", ".dropdown-menu", function (e) {
    $(this).parent().is(".show") && e.stopPropagation();
});

cela fonctionne en 2018

4
waza123
$('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 qu'il soit le plus intelligent et le plus fonctionnel possible. il se ferme maintenant lorsque vous survolez le navigateur, restant ouvert tant que vous y êtes. simplement parfait.

4
Luca Filosofi

Avec Angular2 Bootstrap, vous pouvez utiliser nonInput pour la plupart des scénarios:

<div dropdown autoClose="nonInput">

nonInput - (valeur par défaut) ferme automatiquement la liste déroulante lorsque vous cliquez sur l'un de ses éléments - tant que l'élément sur lequel vous cliquez n'est pas une entrée ou une zone de texte.

https://valor-software.com/ng2-bootstrap/#/dropdowns

3
Nir Soudry

Comme par exemple Bootstrap 4 Alpha a cet événement de menu. Pourquoi ne pas utiliser? 

// PREVENT INSIDE MEGA DROPDOWN
$('.dropdown-menu').on("click.bs.dropdown", function (e) {
    e.stopPropagation();
    e.preventDefault();                
});
3
Frank Thoeny

Vous pouvez arrêter de cliquer sur le menu déroulant et ensuite réimplémenter manuellement les contrôles du carrousel à l’aide des méthodes javascript carousel .

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

Voici le jsfiddle .

3
Neil

Bootstrap a résolu ce problème lui-même en prenant en charge les balises <form> dans les menus déroulants. Leur solution est assez simple et vous pouvez la lire ici: https://github.com/twbs/bootstrap/blob/v4-dev/js/src/dropdown.js

Cela se résume à empêcher la propagation au niveau de l'élément de document et ce, uniquement pour les événements de type 'click.bs.dropdown.data-api' qui correspondent au sélecteur '.dropdown .your-custom-class-for-keep-open-on-click-elements'.

Ou en code

$(document).on('click.bs.dropdown.data-api', '.dropdown .keep-open-on-click', (event) => {
    event.stopPropagation();
});
2
Daniel Schlaug

Je sais que cette question était spécifiquement pour jQuery, mais pour toute personne utilisant ce problème avec AngularJS, 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. 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>
2
jtate

[Bootstrap 4 Alpha 6] [Rails] Pour le développeur Rails, e.stopPropagation() conduira à un comportement indésirable pour link_to avec data-method différent de get puisqu'il retournera par défaut toute votre demande sous la forme 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>

2
Jacky Tong

J'ai modifié la réponse de @ Vartan pour que cela fonctionne avec Bootstrap 4.3. Sa solution ne fonctionne plus avec la dernière version car la propriété target renvoie toujours la racine div de la liste déroulante, quel que soit l'endroit où le clic a été placé.

Voici le code:

$('.dropdown-keep-open').on('hide.bs.dropdown', function (e) {
  if (!e.clickEvent) {
    // There is no `clickEvent` property in the `e` object when the `button` (or any other trigger) is clicked. 
    // What we usually want to happen in such situations is to hide the dropdown so we let it hide. 
    return true;
  }

  var target = $(e.clickEvent.target);

  return !(target.hasClass('dropdown-keep-open') || target.parents('.dropdown-keep-open').length);
});
<div class="dropdown dropdown-keep-open">
  <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    Dropdown button
  </button>
  <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
    <a class="dropdown-item" href="#">Action</a>
    <a class="dropdown-item" href="#">Another action</a>
    <a class="dropdown-item" href="#">Something else here</a>
  </div>
</div>
2
Konrad Kalemba

Dans le contenu .dropdown, placez 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. Désormais, le conteneur hérite de la classe keep-open et check avant sa fermeture.

La solution de travail la plus simple pour moi est:

  • ajout de la classe keep-open à des éléments qui ne devraient pas causer 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');
});
1
Gee-Bee

Je sais qu’il existe déjà une réponse suggérant d’utiliser un formulaire, mais le balisage fourni n’est pas correct/idéal. Voici la solution la plus simple, aucun javascript nécessaire et cela ne casse pas votre liste déroulante. Fonctionne avec Bootstrap 4.

<form class="dropdown-item"> <!-- Your elements go here --> </form>

1
Radu Ciobanu

J'ai trouvé qu'aucune des solutions ne fonctionnait comme j'aimerais utiliser la navigation 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;
       });
1
Mike

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 du bootstrap . Vous pouvez fournir l'une des valeurs suivantes pour auto-close:

  1. toujours - (valeur par défaut) ferme automatiquement la liste déroulante lorsque l'utilisateur clique sur l'un de ses éléments.

  2. outsideClick - ferme automatiquement la liste déroulante uniquement lorsque l'utilisateur clique sur un élément ne figurant pas dans la liste déroulante.

  3. disabled - désactive la fermeture automatique

Jetez un coup d'oeil au plunkr suivant:

http://plnkr.co/edit/gnU8M2fqlE0GscUQtCWa?p=preview

Ensemble

uib-dropdown auto-close="disabled" 

J'espère que cela t'aides :)

1
ashwaniKumar

Vous pouvez avoir des problèmes si vous utilisez la méthode return false ou stopPropagation (), car vos événements seront interrompus. Essayez ce code, ça marche très bien:

$(function() {
    $('.dropdown').on("click", function (e) {
            $('.keep-open').removeClass("show");
    });
    $('.dropdown-toggle').on("click", function () {
            $('.keep-open').addClass("show");
    });

    $( ".closeDropdown" ).click(function() {
        $('.dropdown').closeDropdown();
    });
});
jQuery.fn.extend({
    closeDropdown: function() {
        this.addClass('show')
            .removeClass("keep-open")
            .click()
            .addClass("keep-open");
    }
  });

En HTML:

<div class="dropdown keep-open" id="search-menu" >
    <button  class="btn dropdown-toggle btn  btn-primary" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    <i class="fa fa-filter fa-fw"></i> 
    </button>
    <div class="dropdown-menu">
        <button class="dropdown-item" id="opt1" type="button">Option 1</button>
        <button class="dropdown-item" id="opt2" type="button">Option 2</button>
        <button type="button" class="btn btn-primary closeDropdown">Close</button>
    </div>
</div>

Si vous voulez fermer le dropdrown:

`$('#search-menu').closeDropdown();`
0
Hchab
$(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')
    });
});
0
SNC

Pour fermer la liste déroulante uniquement si un événement click a été déclenché en dehors de la liste déroulante d'amorçage, voici 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>
0
Dudi

Dans Bootstrap 4, vous pouvez également le faire:

$('#dd-link').on('hide.bs.dropdown', onListHide)

function onListHide(e)
{
  if(e.clickEvent && $.contains(e.relatedTarget.parentNode, e.clickEvent.target)) {
  e.preventDefault()
  }
}

#dd-link est l'élément d'ancrage ou le bouton qui possède la propriété data-toggle="drowndown".

0
Daniel

Vous devez ajouter "hold-on-click" à la classe "menu déroulant"

0
chakroun yesser

Je l'ai fait avec ceci:

$(element).on({
    'mouseenter': function(event) {
        $(event.currentTarget).data('mouseover', true);
    },
    'mouseleave': function(event) {
        $(event.currentTarget).data('mouseover', false);
    },
    'hide.bs.dropdown': function (event) {
        return !$(event.currentTarget).data('mouseover');
    }
});
0
William

Cela m'a aidé

$('.dropdown-menu').on('click', function (e) {
     if ($(this).parent().is(".open")) {
         var target = $(e.target);
         if (target.hasClass("keepopen") || target.parents(".keepopen").length){
                    return false; 
                }else{
                    return true;
                }
            }            
});

Votre élément de menu déroulant doit ressembler à ceci (notez les classes dropdown-menu et keepopen.

<ul role="menu" class="dropdown-menu topmenu-menu eserv_top_notifications keepopen">

Le code ci-dessus empêche de miser sur le <body> complet, mais sur l'élément specfic avec la classe dropdown-menu.

J'espère que ça aide quelqu'un.

Merci.

0
Anjana Silva

Vous pouvez passer par le code ci-dessous pour résoudre ce problème.

$(document).on('click.bs.dropdown.data-api', '.keep_it_open', function (e) {
  e.stopPropagation();
});
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css" rel="stylesheet"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>


<div class="dropdown keep_it_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>
0
user2991213