php-dev
php-dev

Reputation: 7156

Avoid dropdown menu close on click inside

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:

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.

Upvotes: 402

Views: 503324

Answers (30)

Vartan
Vartan

Reputation: 677

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

Upvotes: 51

S1awek
S1awek

Reputation: 1783

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

Upvotes: 66

Tom James
Tom James

Reputation: 304

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

Upvotes: 9

NIKHIL CHANDRA ROY
NIKHIL CHANDRA ROY

Reputation: 1087

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

Upvotes: 54

Ivan Kara
Ivan Kara

Reputation: 139

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

Upvotes: 8

Ashok Sharma
Ashok Sharma

Reputation: 1

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

Upvotes: 0

Mr. Smit
Mr. Smit

Reputation: 2532

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

Upvotes: 10

Thulasiram
Thulasiram

Reputation: 8552

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>

Upvotes: 1

Ramast
Ramast

Reputation: 7709

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

Upvotes: 6

TheVigilant
TheVigilant

Reputation: 720

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>

Upvotes: 18

devGarber
devGarber

Reputation: 17

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>

Upvotes: 1

Daniel
Daniel

Reputation: 1531

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.

Upvotes: -1

chakroun yesser
chakroun yesser

Reputation: 1487

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

Upvotes: -3

Konrad Kalemba
Konrad Kalemba

Reputation: 1229

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>

Upvotes: 12

Hchab
Hchab

Reputation: 11

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();`

Upvotes: 0

Anjana Silva
Anjana Silva

Reputation: 9201

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.

Upvotes: 2

William
William

Reputation: 305

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

Upvotes: 1

Daniel Schlaug
Daniel Schlaug

Reputation: 1514

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

Upvotes: 7

Bawantha
Bawantha

Reputation: 4018

This might help:

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

Upvotes: 45

Radu Ciobanu
Radu Ciobanu

Reputation: 748

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>

Upvotes: 3

Dudi
Dudi

Reputation: 3079

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>

Upvotes: 0

julioalberto64
julioalberto64

Reputation: 23

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

});

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

Upvotes: 1

ashwaniKumar
ashwaniKumar

Reputation: 87

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

Upvotes: 1

Kerry Johnson
Kerry Johnson

Reputation: 1024

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/

Upvotes: 13

Jacky Tong
Jacky Tong

Reputation: 21

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

Upvotes: 2

Mike B
Mike B

Reputation: 1446

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

Upvotes: 1

Terry Lin
Terry Lin

Reputation: 2599

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

This may work for any conditions.

Upvotes: 24

Nir Soudry
Nir Soudry

Reputation: 439

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

Upvotes: 2

Gee-Bee
Gee-Bee

Reputation: 3195

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

Upvotes: 1

Related Questions