Avoid dropdown menu close on click inside
Asked Answered
A

40

395

I have a Twitter Bootstrap dropdown menu. As all Twitter Bootstrap users know, the dropdown menu closes on click (even clicking inside it).

To avoid this, I can easily attach a click event handler on the dropdown menu and simply add the famous 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>

This looks easy and a very common behavior, however, and since carousel-controls (as well as carousel indicators) event handlers are delegated to the document object, the click event on these elements (prev/next controls, ...) will be “ignored”.

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

Relying on Twitter Bootstrap dropdown hide/hidden events is not a solution for the following reasons:

  • The provided event object for both event handlers does not give reference to the clicked element
  • I don't have control over the dropdown menu content so adding a flag class or attribute is not possible

This fiddle is the normal behavior and this fiddle is with event.stopPropagation() added.

Update

Thanks to Roman for his answer. I also found an answer that you can find below.

Afflux answered 1/8, 2014 at 21:59 Comment(4)
1. Your jsfiddle is not working. 2. What exactly you want to achieve?Pinchas
@paulalexandru, Updated, added two fiddle. One default behavior, and one with modification. Try clicking on the next & previous button, or on indicators. For the first example, the menu hides & the carousel slides. or the second example : The menu stays open, but the carousel didn't slide since the event propagation has been stopped.Afflux
@Pinchas Got it, Right?Afflux
@php-dev: i have updated it again for sake of challenge, now it is perfect... see the demo.Nyssa
L
420

Removing the data attribute data-toggle="dropdown" and implementing the open/close of the dropdown can be a solution.

First by handling the click on the link to open/close the dropdown like this :

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

and then listening the clicks outside of the dropdown to close it like this :

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

Here is the demo : http://jsfiddle.net/RomaLefrancois/hh81rhcm/2/

Lux answered 8/8, 2014 at 4:32 Comment(18)
This solution is working fine. The bounty should be awarded to you if there is no other answer.Afflux
You seem also to be the only one who understood the situation/requirement :)Afflux
Your solution is working, generic and also "popular". The bounty should be awarded to your answer. I also found an answer, look at it below ;)Afflux
Just a tiny update: $('a.dropdown-toggle:not([data-toggle])').on('click', function (e) { var a = $(e.delegateTarget); var li = a.parent(); li.parent().find('.open').removeClass('open'); li.addClass('open'); });Sangsanger
and what with multi-item in primary navigation ? e.g. jsfiddle.net/cichy380/hh81rhcm/61 (if you click Menu item 1 and after Menu item 2)Demeter
@Cichy...just modify the first handler like this...$('li.dropdown.mega-dropdown a').on('click', function (event) { $('li.dropdown.mega-dropdown.open').removeClass('open'); $(this).parent().toggleClass('open'); });Maldon
$(this).parent().toggleClass('open'); didn't work for me, I used this instead $(this).parents('.btn-group').toggleClass('open');Mcquoid
This preferred answer is a hack. wow!!! Here is a simpler BOOTSTRAP 4 ALPHA answer to prevent inside click in a mega-dropdown. // PREVENT INSIDE CLICK DROPDOWN $('.dropdown-menu').on("click.bs.dropdown", function (e) { e.stopPropagation(); e.preventDefault(); });Pyne
@FrankThoeny your example works fine in Bootstrap 3 too, had similar issue and works perfectly fine. "Roma" you could add this to the answer as well. Easier and cleaner. ex: attach a class to links and then do $('.LINK_CLASS').on('click', function(e) { e.stopPropagation() });Midterm
This one didn't work for me so I published my own answer down the page which fixed the problem without bringing any side problem.Tollman
e.stopPropagation() is the best solution.Postdate
can you please help me to fix this #51553212 @LuxNatatorial
You need to add " mega-dropdown " class to that li you removed the data-toggle="dropdown". in order for it to workTrifolium
This solution does not respect aria-expended attribute.Exertion
Thanks, @nacholibre - your comment works for me with just adding stopPropagationBoggs
For bootstrap 5 change data-toggle attrib to data-bs-toggle and use code like this $("#idOfDropdown").dropdown('toggle');Extroversion
@FrankThoeny Please read carefully the requirement! I don't need the dropdown to close, but I need other behavious to keep working (such as carousel slides, tabs navigation, ... whatever )Afflux
Just to add a minor improvement: use document.addEventListener('click', function(e) {...}, true) to fire the listener during the capturing phase. I noticed that if there are two dropdown buttons, clicking on another dropdown button does not close the current dropdown without setting useCapture to true, possibly because the default dropdown toggle stops event propagation.Acanthocephalan
H
449

This should help as well

$(document).on('click', 'someyourContainer .dropdown-menu', function (e) {
  e.stopPropagation();
});
Hitandmiss answered 3/10, 2015 at 12:44 Comment(10)
That's the most common solution, but I have a different requirement. Please carefully read my question :)Afflux
@HaBa-Media I've even talked about this solution in my question second line.Afflux
This should definitely be the accepted answer. Cleaner, shorter and doesn't disable normal link behaviour which other answers doCrush
simple, elegant and pretty usefull. Just saved me lots of time exploring bootstrap javascript/elements graph. Thanks a lot.Thallus
This will not work if the 'click' event is supposed to trigger further JS actions. The dropdown will not close yes, but all the other events triggered by the click event won't fire anymore.Splenitis
Best solution indeed. I personally used '.dropdown-menu :not(.dropdown-item)' so that clicking on a dropdown-item does close the popup, but clicking on a dropdown-header does not.Exscind
Everyone needs to stop saying this is the best solution, IT IS NOT. The OP addressed this in the question and correctly does not want to use this solution, so this does not answer OP's question. This is begging for a future bug that can be very difficult to debug. Let's say a future dev comes along and attaches a click handler to this menu. The handler will never fire, because this solution has stopped propagation. The future dev will go crazy trying to understand why this isn't working. Just because code works doesn't mean it's right or the best way.Madalynmadam
/**with querySelectorAll => */ document.querySelectorAll('.dropdown-menu').forEach(function (el){ el.addEventListener('click', function (ev) { ev.stopPropagation() }) })Antevert
Problems appear, if you have checkboxes inside.Veator
if you are using bootstrap 5 then it's very easy getbootstrap.com/docs/5.0/components/dropdowns/…Localism
L
420

Removing the data attribute data-toggle="dropdown" and implementing the open/close of the dropdown can be a solution.

First by handling the click on the link to open/close the dropdown like this :

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

and then listening the clicks outside of the dropdown to close it like this :

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

Here is the demo : http://jsfiddle.net/RomaLefrancois/hh81rhcm/2/

Lux answered 8/8, 2014 at 4:32 Comment(18)
This solution is working fine. The bounty should be awarded to you if there is no other answer.Afflux
You seem also to be the only one who understood the situation/requirement :)Afflux
Your solution is working, generic and also "popular". The bounty should be awarded to your answer. I also found an answer, look at it below ;)Afflux
Just a tiny update: $('a.dropdown-toggle:not([data-toggle])').on('click', function (e) { var a = $(e.delegateTarget); var li = a.parent(); li.parent().find('.open').removeClass('open'); li.addClass('open'); });Sangsanger
and what with multi-item in primary navigation ? e.g. jsfiddle.net/cichy380/hh81rhcm/61 (if you click Menu item 1 and after Menu item 2)Demeter
@Cichy...just modify the first handler like this...$('li.dropdown.mega-dropdown a').on('click', function (event) { $('li.dropdown.mega-dropdown.open').removeClass('open'); $(this).parent().toggleClass('open'); });Maldon
$(this).parent().toggleClass('open'); didn't work for me, I used this instead $(this).parents('.btn-group').toggleClass('open');Mcquoid
This preferred answer is a hack. wow!!! Here is a simpler BOOTSTRAP 4 ALPHA answer to prevent inside click in a mega-dropdown. // PREVENT INSIDE CLICK DROPDOWN $('.dropdown-menu').on("click.bs.dropdown", function (e) { e.stopPropagation(); e.preventDefault(); });Pyne
@FrankThoeny your example works fine in Bootstrap 3 too, had similar issue and works perfectly fine. "Roma" you could add this to the answer as well. Easier and cleaner. ex: attach a class to links and then do $('.LINK_CLASS').on('click', function(e) { e.stopPropagation() });Midterm
This one didn't work for me so I published my own answer down the page which fixed the problem without bringing any side problem.Tollman
e.stopPropagation() is the best solution.Postdate
can you please help me to fix this #51553212 @LuxNatatorial
You need to add " mega-dropdown " class to that li you removed the data-toggle="dropdown". in order for it to workTrifolium
This solution does not respect aria-expended attribute.Exertion
Thanks, @nacholibre - your comment works for me with just adding stopPropagationBoggs
For bootstrap 5 change data-toggle attrib to data-bs-toggle and use code like this $("#idOfDropdown").dropdown('toggle');Extroversion
@FrankThoeny Please read carefully the requirement! I don't need the dropdown to close, but I need other behavious to keep working (such as carousel slides, tabs navigation, ... whatever )Afflux
Just to add a minor improvement: use document.addEventListener('click', function(e) {...}, true) to fire the listener during the capturing phase. I noticed that if there are two dropdown buttons, clicking on another dropdown button does not close the current dropdown without setting useCapture to true, possibly because the default dropdown toggle stops event propagation.Acanthocephalan
W
55

In the new Bootstrap 5 the solution is trivially simple.

Quote from the documentation page:

By default, the dropdown menu is closed when clicking inside or outside the dropdown menu. You can use the autoClose option to change this behavior of the dropdown.

In addition to the default behavior, we have 3 options available here:

  • Clickable outside: data-bs-auto-close="outside"
  • Clickable inside: data-bs-auto-close="inside"
  • Manual close: data-bs-auto-close="false"

E.g.:

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

More info: https://getbootstrap.com/docs/5.1/components/dropdowns/#auto-close-behavior

Wordy answered 6/10, 2021 at 7:57 Comment(4)
Best answer for anyone using Bootstrap v5!!Calefaction
Fantastic! I knew bootstrap had to have something for this. Seemed like a trivial thing for them to include since this kind of behavior is asked for so much in previous versions of BootstrapSolus
Will it support in BS4 ?Olympias
@ PRANAV unfortunately it will notWordy
P
54

The absolute best answer is to put a form tag after the class dropdown-menu

so your code is

<ul class="dropdown-menu">
  <form>
    <li>
      <div class="menu-item">bla bla bla</div>
    </li>
  </form>
</ul>
Plagiarize answered 11/12, 2015 at 4:43 Comment(15)
this broke my dropdownRamsgate
functionally worked splendidly for me, but broke the stylingFrankenstein
this was the fastest solution for meAle
This isn't a best answer :) an <li> element should not be inside a <form> element.Raze
See Илья Баранов's answer, where bootstrap stops a click for dropdown with a form. It can be at any level, so the form could go inside the li. Still, this probably breaks quite a few layouts.Yevette
This is not a semanticlly correct solution - the only valid child of a UL is an LI - if anything the form tags should be inside the li tags - but that may not allwo the desired action - either way a ul > form > li is not correct markupPrism
Really great! Who could realised that form tag will prevent closing )))) Thanks a lot!Mortarboard
Thnaks for this fast solutionBlooper
Again menu closed while clicking top or down on padding of ul :D. So I removed padding of ul and added same padding on form tag. Isn't the best answer but wow it is not using any script.Helotry
This is not a correct solution.Why a form?can you explain?Strabismus
If something works, how can it NOT be a correct solution? Why it works is because bootstrap js and jquery both manipulate DOM and neither really care about semantics.Plagiarize
I've replace ul and li tags with div and used form, it's still working fine and now it's valid html too. As for why it works for form because bootstrap support showing a form inside a dropdown (for example login form), in that case it doesn't make sense to hide the popup when clicking on an input field like your usernameAristocratic
Put your entire <ul> inside <form class="dropdown dropdown-item">Selfanalysis
This simply works, other answers are simply too much work and didn't work.Squarrose
Or just replace <ul> and <li> with <div>.Southeastwardly
B
49

Bootstrap provides the following function:

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

Therefore, implementing this function should be able to disable the dropdown from closing.

$('#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;
    }
});
Biblicist answered 1/8, 2014 at 22:6 Comment(16)
Sorry, but I just don't want it to close if clicked inside only, not always.Afflux
In that case, use the event parameter to determine if the target was one of your carousel buttons. function(e){ if(e.target.hasClass('carousel-control'){ return false; } }Tinstone
@NickG. Not a bad idea, but there are other elements with delegated events : ol > li items (carousel indicators), ... Testing against them all looks a bit weird solution. Doesn't it?Afflux
See my new revision. You should be able to add a class "hasopen" to your elements, and they and their parents will cancel the dropdown from closingBiblicist
@Vartan, Your solution is awesome if we assume that the dropdown menu doesn't change. The carousel controls are just an example. I am dynamically generating the content (It can be anything and not only a carousel, ...) so adding a keepopen class is not really possible.Afflux
@Vartan, Also the event target property is the li.dropdown, the relatedTarget is the a.dropdown-toggle. So you can't referer to the clicked element within the hide event handler.Afflux
@NickG I found one solution. Check it below :)Afflux
hasClass takes a classname, not a CSS selector. target.hasClass(".keepopen") should be target.hasClass("keepopen"). Otherwise the code won’t work.Modulator
I have scanned the internet and this is the most intelligent solution by far, for this reason... msot other methods call for an approach that uses stopping the default event or stopping propagation. these approaches are very bad as they will break any other triggered events inside the drop down (i.e. my jquery mobile form elements) using this approach allows you to specifically identify when the menu itself is closing and allow you to take action on that event alone. this answer needs so many stars....Ilan
@Ilan How much did it take you to scan the internet? (rofl)Afflux
This should be the accepted answer. Much better than a click handler on the whole body, especially if you're using Angular/React/etc.Baltazar
Maybe this used to work.. but with bootstrap 4, e.target is always the dropdown itself. Not the clicked target. Even if you click outside the dropdowns, e.target is still the dropdown. So you cannot perform this kind of check. Anyone see a way around this?Squint
Can some one supply the HTML that would match the JS solution above, I'm struggling to get this to work with BS3?Louvre
@Squint I modified Vartan's answer to make it work with BS4. Have a look: #25089797Favata
It worked for me when I had the bootstrap-select button click which was inside the dropdown menu of bootstrap 4 navbar.Herschelherself
This solution worked for me. And also preferable because it feels like the "bootstrap way" of handling things.Infamous
F
47

I just add onclick event like below to not close dropdown-menu.

 <div class="dropdown-menu dropdown-menu-right" onclick="event.stopPropagation()" aria-labelledby="triggerId">
Furfural answered 29/1, 2021 at 9:18 Comment(5)
This seems to be a correct answer for [email protected]Snoop
This worked for me like a charm, thanksHydrophobia
this should be on top this is by far the simplest and less code answerSieve
Just to point out that, while the other good answers are similar, this approach is by far the easiest to implement and works as expected.Interested
If you have a form inside the dropdown-menu it will not allow you to select any radio ro checkboxes.Millwork
C
45

This might help:

$("dropdownmenuname").click(function(e){
   e.stopPropagation();
})
Converter answered 19/4, 2018 at 21:18 Comment(1)
This is definitely the simplest/best answer if you want a user to interact with a UI element present on the dropdown (e.g. radio buttons) and don't want the menu to close when they toggle options.Rapeseed
A
33

I also found a solution.

Assuming that the Twitter Bootstrap Components related events handlers are delegated to the document object, I loop the attached handlers and check if the current clicked element (or one of its parents) is concerned by a delegated event.

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

Hope it helps any one looking for a similar solution.

Thank you all for your help.

Afflux answered 11/8, 2014 at 21:50 Comment(4)
Wow!!! worked perfectly! Was in trouble for a week , I am using mCustomeScrollbar and after clicking on scrollbar in drop down it was closing it, your script worked and now its working fine! Thanks a million.Dilantin
works great! how does one extend this, so that a user can still press "ESC" (or even click outside) on keyboard to close the DD?Gielgud
@MaggewDotCom The ESC key press should still work. The same for clicking outside ...Afflux
The only downside of this solution is that it doesn't allow nested dropdowns (say you have a dropright inside a dropdown). Is there a workaround for this case?Barricade
O
24
$('body').on("click", ".dropdown-menu", function (e) {
    $(this).parent().is(".open") && e.stopPropagation();
});

This may work for any conditions.

Obscurant answered 4/12, 2016 at 13:51 Comment(2)
this should be the correct answer as it uses fewer lines of code rather than the selected answerFelloe
@MuhammadOmerAslam Nah! As it doesn't fulfill my requirementAfflux
C
18

I tried this simple thing and it worked like a charm.

I changed the dropdown-menu element from <div> to <form> and it worked well.

<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>
California answered 21/9, 2019 at 17:26 Comment(1)
For me this only worked with the surrounding <div class="dropdown"></div>, but then very well.Funiculate
I
13

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>

Demo:

Generic: https://jsfiddle.net/kerryjohnson/omefq68b/1/

Your demo with this solution: http://jsfiddle.net/kerryjohnson/80oLdtbf/101/

Ifc answered 21/4, 2017 at 19:34 Comment(0)
F
12

I modified @Vartan's answer to make it work with Bootstrap 4.3. His solution doesn't work anymore with the latest version as target property always returns dropdown's root div no matter where the click was placed.

Here is the 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>
Favata answered 30/3, 2019 at 21:22 Comment(3)
oneline: $('.dropdown-keep-open').on('hide.bs.dropdown', ev => !(ev.clickEvent && $(ev.target).has(ev.clickEvent.target).length))Protrusive
@MatveevDmitriy I'm sorry but no one, simply no one can read this one liner. Writing code this way is imho a bad practice.Flummox
The last line can be simplified using return !target.closest('.dropdown-keep-open').length). That said, this is a great solution! Found this needed to solve this problem.Madalynmadam
S
10
$('body').on("click", ".dropdown-menu", function (e) {
    $(this).parent().is(".show") && e.stopPropagation();
});
Sidell answered 29/7, 2018 at 14:2 Comment(1)
First, thank you for your answer. The question is already answered. Also, I have a different requirement as described in the question than the common requirement.Afflux
C
9

Bootstrap 5

If anyone comes to this via Google wanting a Bootstrap 5 version like I was, it's built in by adding data-bs-auto-close="outside". Note the option is autoClose but when passing as a data attribute the camelcasing is removed & separated by a dash.

I have a collapse widget in a dropdown & adding data-bs-auto-close="outside" to the parent data-bs-toggle="dropdown" trigger keeps the dropdown open while the collapse is toggled.

See official Bootstrap docs: https://getbootstrap.com/docs/5.1/components/dropdowns/#options And this codepen for example code (not my pen): https://codepen.io/SitePoint/pen/BaReWGe

Cavalier answered 31/10, 2021 at 7:25 Comment(0)
P
7

Like for instance Bootstrap 4 Alpha has this Menu Event. Why not use?

// PREVENT INSIDE MEGA DROPDOWN
$('.dropdown-menu').on("click.bs.dropdown", function (e) {
    e.stopPropagation();
    e.preventDefault();                
});
Pyne answered 29/3, 2016 at 3:55 Comment(2)
e.preventDefault(); blocks links, you can skip itNolde
@czachor you're Right! e.preventDefault(); Prevent a link from following the URL.Pyne
P
7

Bootstrap has solved this problem themselves in their support for <form> tags in dropdowns. Their solution is quite graspable and you can read it here: https://github.com/twbs/bootstrap/blob/v4-dev/js/src/dropdown.js

It boils down to preventing propagation at the document element and doing so only for events of type 'click.bs.dropdown.data-api' that match the selector '.dropdown .your-custom-class-for-keep-open-on-click-elements'.

Or in code

$(document).on('click.bs.dropdown.data-api', '.dropdown .keep-open-on-click', (event) => {
    event.stopPropagation();
});
Pseudocarp answered 7/10, 2018 at 19:54 Comment(1)
Thank you thank you thank you for the link to the source. For others wondering why the form approach from the examples isn't working for you - I was using class dropleft, but it also needs dropdown in order for the form-selector to match.Blimey
F
7

You can also use form tag. Example:

<div class="dropdown-menu">
    <form>
        Anything inside this wont close the dropdown!
        <button class="btn btn-primary" type="button" value="Click me!"/>
    </form>
    <div class="dropdown-divider"></div>
    <a class="dropdown-item" href="#">Clik this and the dropdown will be closed</a>
    <a class="dropdown-item" href="#">This too</a>
</div>

Source: https://getbootstrap.com/docs/5.0/components/dropdowns/#forms

Futuristic answered 22/1, 2021 at 6:18 Comment(0)
M
6

I've got a similar problem recently and tried different ways to solve it with removing the data attribute data-toggle="dropdown" and listening click with event.stopPropagation() calling.

The second way looks more preferable. Also Bootstrap developers use this way. In the source file I found initialization of the dropdown elements:

// 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);

So, this line:

.on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })

suggests you can put a form element inside the container with class .dropdown to avoid closing the dropdown menu.

Mackler answered 27/11, 2015 at 17:51 Comment(0)
A
6

You could simply execute event.stopPropagation on click event of the links themselves. Something like this.

    $(".dropdown-menu a").click((event) => {
         event.stopPropagation()
         let url = event.target.href
         //Do something with the url or any other logic you wish
    })

Edit: If someone saw this answer and is using react, it will not work. React handle the javascript events differently and by the time your react event handler is being called, the event has already been fired and propagated. To overcome that you should attach the event manually like that

handleMenuClick(event) {
   event.stopPropagation()
   let menu_item = event.target
   //implement your logic here.
}
componentDidMount() {
    document.getElementsByClassName("dropdown-menu")[0].addEventListener(
        "click", this.handleMenuClick.bind(this), false)
   }
}
Aristocratic answered 29/11, 2019 at 21:5 Comment(0)
J
5

You can stop click on the dropdown from propagating and then manually reimplement the carousel controls using carousel javascript methods.

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

Here is the jsfiddle.

Jehu answered 11/8, 2014 at 19:56 Comment(1)
Your example works fine but the problem is that the dropdown can hold any content and not only images carousel ...Afflux
N
4
$('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');
});

i have updated it once again to be the smartest and functional as possible. it now close when you hover outside the nav, remaining open while you are inside it. simply perfect.

Nyssa answered 11/8, 2014 at 20:41 Comment(5)
Your solution is smart due to its shortness, however, after added the class show, the dropdown doesn't close on click outside, and we need to click on the dropdown-toggle before ... +1 anywayAfflux
i have updated it, look at the demo. it is the best cause don't check against the body click event everytime. affect only element inside each single nav element not outside of it.Nyssa
My solution also don't rely on body click event ^^Afflux
yes, it relay in part on body click event, it try to check what element has been clicked trought the body delegation. $('body').on('click', function (e) {Nyssa
Nah! That's not my answer. My answer is the last posted one.Afflux
H
3

I know there already is a previous answer suggesting to use a form but the markup provided is not correct/ideal. Here's the easiest solution, no javascript needed at all and it doesn't break your dropdown. Works with Bootstrap 4.

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

Heptode answered 18/3, 2018 at 7:29 Comment(2)
@RosdiKasim I have it working fine on a website with Bootstrap 4.0.0. Haven't tested with 4.1Heptode
Actually it should be like : <form class="dropdown dropdown-item">Selfanalysis
G
2

With Angular2 Bootstrap, you can use nonInput for most scenarios:

<div dropdown autoClose="nonInput">

nonInput - (default) automatically closes the dropdown when any of its elements is clicked — as long as the clicked element is not an input or a textarea.

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

Gregarious answered 15/8, 2016 at 7:13 Comment(0)
I
2

[Bootstrap 4 Alpha 6][Rails] For rails developer, e.stopPropagation() will lead to undesirable behavior for link_to with data-method not equal to get since it will by default return all your request as get.

To remedy this problem, I suggest this solution, which is 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>
Iyeyasu answered 29/3, 2017 at 7:53 Comment(0)
C
2

This helped me,

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

Your drop down menu element needs to be like this, (take a note of the classes dropdown-menu and keepopen.

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

The above code prevents biding on the whole <body>, instead to the specfic element with the class dropdown-menu.

Hope this helps someone.

Thanks.

Cuneate answered 22/2, 2019 at 15:12 Comment(1)
Thanks Updated As Per Bootstrap 4 $('.dropdown-menu[data-handledropdownclose="true"]').on('click', function (e) { if ($(this).parent().is(".show")) { var target = $(e.target); return (target.hasClass("CloseDropDown") || target.parents(".CloseDropDown").length); } });Salep
V
1

I know this question was specifically for jQuery, but for anyone using AngularJS that has this problem you can create a directive that handles this:

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
          });
        }
    };
});

Then just add the attribute dropdown-prevent-close to your element that is triggering the menu to close, and it should prevent it. For me, it was a select element that automatically closed the menu:

<div class="dropdown-menu">
  <select dropdown-prevent-close name="myInput" id="myInput" ng-model="myModel">
    <option value="">Select Me</option>
  </select>
</div>
Vitrification answered 14/6, 2016 at 16:26 Comment(1)
<div class="dropdown-menu" ng-click="$event.stopPropagation()"> works for me in 1.2.xLeisure
A
1

The simplest working solution for me is:

  • adding keep-open class to elements that should not cause dropdown closing
  • and this piece of code do the rest:
$('.dropdown').on('click', function(e) {
    var target = $(e.target);
    var dropdown = target.closest('.dropdown');
    return !dropdown.hasClass('open') || !target.hasClass('keep-open');
});
Atom answered 14/7, 2016 at 11:25 Comment(1)
can you please help me to fix this #51553212 @AtomNatatorial
S
1

I've found none of the solutions worked as I would like using default bootstrap nav. Here is my solution to this problem:

       $(document).on('hide.bs.dropdown', function (e) {
        if ($(e.currentTarget.activeElement).hasClass('dropdown-toggle')) {
          $(e.relatedTarget).parent().removeClass('open');
          return true;
        }
        return false;
       });
Swaziland answered 3/1, 2017 at 23:14 Comment(0)
S
1

Instead of writing some javascript or jquery code(reinventing the wheel). The above scenario can be managed by bootstrap auto-close option. You can provide either of the values to auto-close:

  1. always - (Default) automatically closes the dropdown when any of its elements is clicked.

  2. outsideClick - closes the dropdown automatically only when the user clicks any element outside the dropdown.

  3. disabled - disables the auto close

Take a look at the following plunkr :

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

Set

uib-dropdown auto-close="disabled" 

Hope this helps :)

Sosa answered 7/6, 2017 at 11:3 Comment(2)
It should be explicitly noted that this is only good for people using ui-bootstrap, which is probably not the case for very many people viewing this question.Lorettalorette
You well described but I think you should write solution as uib-dropdown auto-close="outsideClick". Menu close on click inside is just a little disconfort but menu not close on click outside is quite upsetting.Helotry
P
1

In .dropdown content put the .keep-open class on any label like so:

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

The previous cases avoided the events related to the container objects, now the container inherits the class keep-open and check before being closed.

Palmieri answered 6/9, 2017 at 13:21 Comment(0)
O
1

I did it with this:

$(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');
    }
});
Ocelot answered 6/2, 2019 at 19:12 Comment(0)
H
1

You can go through the below code to solve this.

$(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>
Heavyduty answered 17/7, 2019 at 12:46 Comment(0)
S
1

Bootstrap 4

$('.dropdown-menu[data-handledropdownclose="true"]').on("click.bs.dropdown", function (e) {
    if ($(this).parent().hasClass("show")) {
        var target = $(e.target);

        if (!(target.hasClass("CloseDropDown") || target.parents(".CloseDropDown").length)) {
            e.stopPropagation();
        }
    }
});

<div class="dropdown">
    <button type="button" class="btn-no-border dropdown-toggle" data-toggle="dropdown">
        <img src="~/Content/CSS/CssImages/Icons/usr_icon.png" alt="" title="language" class="float-right" />
    </button>

    <div class="dropdown-menu profile-menu-logout" data-handledropdownclose="true">
        <div class="prof-name">
            <i class="fa fa-user"></i> Hello World
        </div>

        <hr />

        <div>
            <a href="/Test/TestAction" class="CloseDropDown">
                <i class="fa fa-briefcase"></i>
                <span>Test Action</span>
            </a>
        </div>

        <div>
            <nav>
                <ul class="nav-menu-prof padding-0">
                    <li class="menu-has-children">
                        <a href="#">
                            <span class="cyan-text-color">
                                Test 2
                            </span>
                        </a>

                        <ul id="ulList" class="padding-0 pad-left-25">
                            <li>
                                <a href="/Test/Test2" class="action currentmenu"> Test 1 </a>
                                <a href="/Test/Test2" class="action CloseDropDown"> Test 2 </a>
                            </li>
                        </ul>
                    </li>
                </ul>
            </nav>
        </div>

        <div>
            <a href="/Account/Logout" class="cyan-text-color CloseDropDown">
                <i class="fa fa-power-off"></i>
                <span>Logout</span>
            </a>
        </div>
    </div>
</div>
Salep answered 30/4, 2020 at 4:40 Comment(0)
B
0
$(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')
    });
});
Betoken answered 4/3, 2016 at 15:57 Comment(0)
I
0

For closing the dropdown only if a click event was triggered outside the bootstrap dropdown, this is what worked for me:

JS file:

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

HTML file:

<!-- 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>
Irradiate answered 28/12, 2017 at 16:2 Comment(0)
S
0

You may have some problems if you use return false or stopPropagation() method because your events will be interrupted. Try this code, it's works fine:

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

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

If you want to close the dropdrown:

`$('#search-menu').closeDropdown();`
Severus answered 6/3, 2019 at 3:20 Comment(0)
T
0
$(document).click(function (event) {
    $target = $(event.target);
    if ($target.closest('#DivdropFilterListItemsCustomer').length == 1) {
        $('#DivdropFilterListItemsCustomer').addClass('dropdown-menu show');        
    } else {
        $('#DivdropFilterListItemsCustomer').removeClass('dropdown-menu 
           show').addClass('dropdown-menu');
    }
});
DivdropFilterListItemsCustomer is id of drop down 
 [Show id and drop down ][1]

#A2ZCode

Tadzhik answered 15/1, 2021 at 5:28 Comment(1)
Please explain your answer and how it answers the question's concerns.Daric
S
-1
$(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;
    }
});

});

Snowcap answered 14/9, 2017 at 23:5 Comment(1)
Welcome to StackOverflow. Instead of only showing code, try to write a good answer following these points: How to Answer. Also this question already had 19 answers, how does your solution differs from others?Maintain
B
-1

In Bootstrap 4 you can also do this:

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

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

where #dd-link is the anchor element or button that has the data-toggle="drowndown" property.

Blouson answered 13/6, 2019 at 20:52 Comment(0)
M
-3

You need to add "hold-on-click" to "dropdown-menu" class

Marijo answered 22/5, 2019 at 13:0 Comment(1)
...What does .hold-on-click do, and what library does it come from?Postglacial

© 2022 - 2024 — McMap. All rights reserved.