javascript - bootstrap - Evitar cerrar el menú desplegable al hacer clic dentro
bootstrap tabs events (25)
- demostración: http://jsfiddle.net/4y8tLgcp/
$(''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'');
});
Lo he actualizado una vez más para ser el más inteligente y funcional posible. ahora se cierra cuando se desplaza fuera del navegador, permaneciendo abierto mientras está dentro de él. Simplemente perfecto.
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 en él).
Para evitar esto, puedo adjuntar fácilmente 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>
Sin embargo, esto parece fácil y es un comportamiento muy común, y como carousel-controls
eventos carousel-controls
(así como los carousel indicators
) se delegan al objeto del document
, el evento de click
en estos elementos (controles anterior / siguiente , ...) 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 Twitter Bootstrap desplegable hide
/ eventos hidden
no es una solución por las siguientes razones:
- El evento proporcionado para ambos controladores de eventos no le da referencia al elemento en el que se hizo clic
- El contenido del menú desplegable se genera dinámicamente, por lo que no es posible agregar una clase de marca
Este violín es el comportamiento normal y este violín es con el event.stopPropagation()
agregado.
Actualizar
Gracias a Roman por su respuesta . También encontré una respuesta que puedes encontrar a continuación .
Bootstrap ha resuelto este problema ellos mismos en su compatibilidad con las etiquetas <form>
en los menús desplegables. Su solución es bastante comprensible y puede leerla aquí: https://github.com/twbs/bootstrap/blob/v4-dev/js/src/dropdown.js
Se reduce a prevenir la propagación en el elemento del documento y solo lo hace para eventos del tipo ''click.bs.dropdown.data-api''
que coinciden con el selector ''.dropdown .your-custom-class-for-keep-open-on-click-elements''
.
O en codigo
$(document).on(''click.bs.dropdown.data-api'', ''.dropdown .keep-open-on-click'', (event) => {
event.stopPropagation();
});
Como, por ejemplo, Bootstrap 4 Alpha tiene este evento de menú. ¿Por qué no usar?
// PREVENT INSIDE MEGA DROPDOWN
$(''.dropdown-menu'').on("click.bs.dropdown", function (e) {
e.stopPropagation();
e.preventDefault();
});
Con Angular2 Bootstrap, puede usar nonInput para la mayoría de los escenarios:
<div dropdown autoClose="nonInput">
nonInput: (predeterminado) cierra automáticamente el menú desplegable cuando se hace clic en cualquiera de sus elementos, siempre que el elemento en el que se haga clic no sea una entrada o un área de texto.
Eliminar el atributo de data-toggle="dropdown"
e implementar la apertura / cierre de la lista desplegable puede ser una solución.
Primero manejando el clic en el enlace para abrir / cerrar el menú desplegable de la siguiente manera:
$(''li.dropdown.mega-dropdown a'').on(''click'', function (event) {
$(this).parent().toggleClass(''open'');
});
y luego escuchar los clics fuera de la lista desplegable para cerrarla 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/
En el contenido de .dropdown
, coloque la clase .keep-open
en cualquier etiqueta, de esta forma:
$(''.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
}
});
Los casos anteriores evitaron los eventos relacionados con los objetos del contenedor, ahora el contenedor hereda la clase keep-open y se comprueba antes de cerrarse.
En lugar de escribir algún código javascript o jquery (reinventar la rueda). El escenario anterior se puede gestionar mediante la opción de cierre automático de arranque. Puede proporcionar cualquiera de los valores para cerrar automáticamente :
siempre: (Predeterminado) cierra automáticamente el menú desplegable cuando se hace clic en cualquiera de sus elementos.
outsideClick: cierra el menú desplegable automáticamente solo cuando el usuario hace clic en cualquier elemento fuera del menú desplegable.
deshabilitado - desactiva el cierre automático
Echa un vistazo a la siguiente plunkr:
http://plnkr.co/edit/gnU8M2fqlE0GscUQtCWa?p=preview
Conjunto
uib-dropdown auto-close="disabled"
Espero que esto ayude :)
Esto debería ayudar también
$(document).on(''click'', ''someyourContainer .dropdown-menu'', function (e) {
e.stopPropagation();
});
Esto podría ayudar:
$("dropdownmenuname").click(function(e){
e.stopPropagation();
})
La mejor respuesta absoluta es colocar una etiqueta de formulario después del menú desplegable de la clase
entonces tu código es
<ul class="dropdown-menu">
<form>
<li>
<div class="menu-item">bla bla bla</div>
</li>
</form>
</ul>
La solución de trabajo más simple para mí es:
- agregando la clase
keep-open
a los elementos que no deberían causar el cierre desplegable - Y esta pieza de código hace el resto:
$(''.dropdown'').on(''click'', function(e) {
var target = $(e.target);
var dropdown = target.closest(''.dropdown'');
return !dropdown.hasClass(''open'') || !target.hasClass(''keep-open'');
});
No he encontrado ninguna de las soluciones que funcionen como me gustaría usar el bootstrap nav predeterminado. Aquí está mi solución a este problema:
$(document).on(''hide.bs.dropdown'', function (e) {
if ($(e.currentTarget.activeElement).hasClass(''dropdown-toggle'')) {
$(e.relatedTarget).parent().removeClass(''open'');
return true;
}
return false;
});
Para cerrar la lista desplegable solo si se activó un evento de clic fuera de la lista desplegable de arranque, esto es lo que funcionó para mí:
Archivo 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();
}
});
Archivo 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>
Puede dejar de hacer clic en el menú desplegable de propagación y luego volver a implementar manualmente los controles del carrusel utilizando los métodos de javascript del carrusel .
$(''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);
});
Aquí está el jsfiddle .
Sé que esta pregunta fue específicamente para jQuery, pero para cualquier persona que use AngularJS que tenga este problema, puede crear una directiva que maneje esto:
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
});
}
};
});
Luego simplemente agregue el atributo dropdown-prevent-close
a su elemento que está activando el cierre del menú, y debería evitarlo. Para mí, fue un elemento de select
que cerró automáticamente el menú:
<div class="dropdown-menu">
<select dropdown-prevent-close name="myInput" id="myInput" ng-model="myModel">
<option value="">Select Me</option>
</select>
</div>
Sé que ya hay una respuesta anterior que sugiere utilizar un formulario, pero el marcado proporcionado no es correcto / ideal. Aquí está la solución más fácil, no se necesita javascript en absoluto y no rompe su menú desplegable. Funciona con Bootstrap 4.
<form class="dropdown-item"> <!-- Your elements go here --> </form>
También encontré una solución.
Suponiendo que los controladores de eventos relacionados con los Twitter Bootstrap Components
estén delegados al objeto de document
, hago un bucle de los controladores adjuntos y verifico si el evento delegado concierne al elemento en el que se hizo clic (o uno de sus padres).
$(''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 por toda tu ayuda.
Tengo un problema similar recientemente e intenté diferentes maneras de resolverlo eliminando el atributo de data-toggle="dropdown"
y escuchando haciendo click
con el event.stopPropagation()
llamando.
La segunda forma se ve más preferible. También los desarrolladores de Bootstrap usan de esta manera. En el archivo fuente encontré la inicialización de los elementos desplegables:
// 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);
Por lo tanto, esta línea:
.on(''click.bs.dropdown.data-api'', ''.dropdown form'', function (e) { e.stopPropagation() })
sugiere que puede colocar un elemento de form
dentro del contenedor con la clase .dropdown
para evitar cerrar el menú desplegable.
[Bootstrap 4 Alpha 6] [Rails] Para el desarrollador de rieles, e.stopPropagation()
conducirá a un comportamiento no deseado para link_to
con data-method
no igual a get
ya que por defecto devolverá toda su solicitud como get
.
Para remediar este problema, sugiero esta solución, que es universal.
$(''.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>
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, la implementación de esta función debería poder desactivar el menú desplegable de cierre.
$(''#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;
}
});
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/
Su demostración con esta solución: http://jsfiddle.net/kerryjohnson/80oLdtbf/101/
$(''body'').on("click", ".dropdown-menu", function (e) {
$(this).parent().is(".open") && e.stopPropagation();
});
Esto puede funcionar para cualquier condición.
$(''body'').on("click", ".dropdown-menu", function (e) {
$(this).parent().is(".show") && e.stopPropagation();
});
esto funciona 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'')
});
});
$(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;
}
});
});