Búsqueda de sitios web

javascript - Evitar que el menú desplegable se cierre al hacer clic dentro


Tengo un menú desplegable de Twitter Bootstrap. Como todos los usuarios de Twitter Bootstrap saben, el menú desplegable se cierra al hacer clic (incluso al hacer clic dentro de él).

Para evitar esto, puedo fácilmente adjuntar un controlador de eventos de clic en el menú desplegable y simplemente agregar el famoso 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>

Esto parece fácil y un comportamiento muy común, sin embargo, y dado que los controladores de eventos de carousel-controls (así como de carousel indicators) se delegan al objeto document, el evento click en estos elementos (prev/next controles, ...) será "ignorado".

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

Confiar en los eventos hide/hidden del desplegable de Twitter Bootstrap no es una solución por las siguientes razones:

  • El objeto de evento proporcionado para ambos controladores de eventos no da referencia al elemento clicado
  • No tengo control sobre el contenido del menú desplegable, así que agregar una clase o atributo flag no es posible

Este fiddle es el comportamiento normal y este fiddle es con event.stopPropagation() añadido.

Actualizar

Gracias a Roman por su respuesta. También encontré una respuesta que puedes encontrar abajo.

Respuesta #1

Esto también debería ayudar

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

Respuesta #2

Eliminar el atributo de datos data-toggle="dropdown" e implementar la apertura/cierre del desplegable puede ser una solución.

Primero manejando el clic en el enlace para abrir/cerrar el desplegable así:

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

y luego escuchando los clics fuera del desplegable para cerrarlo así:

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

Aquí está la demostración: http://jsfiddle.net/RomaLefrancois/hh81rhcm/2/

Respuesta #3

En el nuevo Bootstrap 5, la solución es trivialmente simple.

Cita de la página de documentación:

Por defecto, el menú desplegable se cierra al hacer clic dentro o fuera del menú desplegable. Puedes usar la opción autoClose para cambiar este comportamiento del menú desplegable.

Además del comportamiento predeterminado, tenemos 3 opciones disponibles aquí:

  • Clicable fuera: data-bs-auto-close="outside"
  • Clicable dentro: data-bs-auto-close="inside"
  • Cierre manual: data-bs-auto-close="false"

Por ejemplo:

<div class="btn-group">
  <button class="btn btn-secondary dropdown-toggle" data-bs-auto-close="inside" type="button" id="dropdownMenuClickableInside" data-bs-toggle="dropdown" aria-expanded="false">
    Clickable inside
  </button>
  <ul class="dropdown-menu" aria-labelledby="dropdownMenuClickableInside">
    <li><a class="dropdown-item" href="#">Menu item</a></li>
    <li><a class="dropdown-item" href="#">Menu item</a></li>
    <li><a class="dropdown-item" href="#">Menu item</a></li>
  </ul>
</div>

Más información: https://getbootstrap.com/docs/5.1/components/dropdowns/#auto-close-behavior

Respuesta #4

Solo agrego un evento onclick como se muestra a continuación para no cerrar el menú desplegable.

 <div class="dropdown-menu dropdown-menu-right" onclick="event.stopPropagation()" aria-labelledby="triggerId">

Respuesta #5

La mejor respuesta absoluta es poner una etiqueta form después del menú desplegable de clases

así que tu código es

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

Respuesta #6

Bootstrap proporciona la siguiente función:

                 | 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.

Por lo tanto, implementar esta función debería poder desactivar que el menú desplegable se cierre.

$('#myDropdown').on('hide.bs.dropdown', function (e) {
    var target = $(e.clickEvent.target);
    if(target.hasClass("keepopen") || target.parents(".keepopen").length){
        return false; // returning false should stop the dropdown from hiding.
    }else{
        return true;
    }
});

Respuesta #7

Esto podría ayudar:

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

Respuesta #8

También encontré una solución.

Suponiendo que los controladores de eventos relacionados con los Componentes de Twitter Bootstrap estén delegados al objeto document, recorro los controladores adjuntos y verifico si el elemento actualmente clicado (o alguno de sus padres) está relacionado con un evento delegado.

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

Espero que ayude a cualquiera que busque una solución similar.

Gracias a todos por su ayuda.

Respuesta #9

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

Esto puede funcionar para cualquier condición.

Respuesta #10

Probé esta cosa simple y funcionó de maravilla.

Cambié el elemento dropdown-menu de <div> a <form> y funcionó bien.

<div class="nav-item dropdown" >
  <a href="javascript:;" class="nav-link dropdown-toggle" data-toggle="dropdown">
   Click to open dropdown
 </a>
 <form class="dropdown-menu   ">
  <ul class="list-group text-black">
     <li class="list-group-item"  >
     </li>
     <li class="list-group-item"   >
     </li>
  </ul>
</form>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>

<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>


<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>


<div class="nav-item dropdown" >
  <a href="javascript:;" class="nav-link dropdown-toggle" data-toggle="dropdown">
   Click to open dropdown
 </a>
 <form class="dropdown-menu   ">
  <ul class="list-group text-black">
     <li class="list-group-item"  >
      List Item 1
     </li>
     <li class="list-group-item"   >
         LI 2<input class="form-control" />
     </li>
     <li class="list-group-item"   >
        List Item 3
     </li>
  </ul>
</form>

Respuesta #11

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>

Demostración:

Genérico: https://jsfiddle.net/kerryjohnson/omefq68b/1/

Tu demostración con esta solución: http://jsfiddle.net/kerryjohnson/80oLdtbf/101/

Respuesta #12

Modifiqué la respuesta de @Vartan para que funcione con Bootstrap 4.3. Su solución ya no funciona con la última versión, ya que la propiedad target siempre devuelve el div raíz del desplegable sin importar dónde se realizó el clic.

Aquí está el código:

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

Respuesta #13

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

Artículos relacionados