angular-bootstrap dropdown on mouseenter and keep dropdown-menu from hiding before being clicked.

Yahya KACEM picture Yahya KACEM · Dec 5, 2014 · Viewed 11.2k times · Source

First, I'm aware of this posts:
Activating bootstrap dropdown menu on hover
Bootstrap Dropdown with Hover
How to make twitter bootstrap menu dropdown on hover rather than click
And others, but still not found the correct solution yet, here's what I did so far.
first I used the is-open attribute from the angular-bootstrap dropdown directive like this:

<span class="dropdown" dropdown is-open="status.isopen">
  <a
    href
    class="dropdown-toggle"
    ng-mouseenter="status.isopen = true"
    ng-mouseleave="status.isopen = false"
  >
    hover me for a dropdown with angular-bootstrap
  </a>
  <ul
    class="dropdown-menu"
  >
    <li ng-repeat="choice in items">
      <a href>{{choice}}</a>
    </li>
  </ul>
</span>

that seemed to work but 2 bugs appeared:

  • the first is when dropdown-toggle element is clicked the dropdown menu is gone clicking again wont bring it back you have to mouseleave then mouse enter the dropdown-tooggle to get the dropdown-menu back.
  • the second is a css/html problem.

Usually the regular css solution for a dropdown is like this:

<a class="css-dropdown">
  hover here with css.
  <div class="css-dropdown-menu">
    <p>item 1</p>
    <p>item 2</p>
    <p>item 3</p>
  </div>
</a>

Notice the dropdown-menu now is inside the dropdown-toggle element which mean when moving with the mouse from the dropdown-toggle to the dropdown-menu it's moving from parent to child, so basically we still hovering over the dropdown-toggle since we are in it's child, which mean the dropdown-menu will still be visible, on other hand, the bootstrap dropdown works with the click event so having the dropdown-menu as a child of the dropdown-toggle is not needed, but now when someone wants to change the behavior to mouseenter/hover once the mouse leaves the dropdown-toggle the dropdown-menu disappear so we no longer have access to the dropdown-menu elements this is visible in this plunker

To fix the first bug, I just removed the dropdown directive then replaced the is-open with ng-class directive like this.
Change this:

<span class="dropdown" dropdown is-open="status.isopen">

to this:

<span class="dropdown" ng-class="{'open': status.isopen}">

The rest stays the same plunker that fixed the first bug.
The second bug is tricky, since the dropdown-menu is no longer a child of the dropdown-toggle the hover effect wont last while moving from the toggle to the menu, so I did this. Changed this:

<ul class="dropdown-menu">

to this:

<ul
  class="dropdown-menu"
  ng-mouseenter="status.isopen = true"
  ng-mouseleave="status.isopen = false"
>

That did it but another bug appeared when clicking the dropdown-menu item it stays open, so I kept hacking by doing this. changed this:

<li ng-repeat="choice in items">

to this:

<li ng-repeat="choice in items" ng-click="status.isopen = false">

That give me the required behavior plunker.
That said, this is not a good solution since a lot of directives are involved here for a simple visual effect, the last plunker I provided contains a css solution with no Bootstrap or AngularJS involved, though it is the required behavior it is not the required html structure or visual result, what I need is to have a space between the dropdown-toggle and the dropdown-menu not a padding of the toggle element just an empty space, which make the css solution not valid in this situation.
So, my question is there a better way of doing this without adding a new plugin/library more clean and easily reusable solution for the hover drop down menu?

Answer

Kenny Ki picture Kenny Ki · Jul 15, 2015

First, have the toggling on the top-most parent element (in this case, the <span>)

<span class="btn-group" dropdown is-open="status.isopen" ng-mouseenter="status.isopen = true" ng-mouseleave="status.isopen = false">
  <a class="btn btn-primary dropdown-toggle" dropdown-toggle>
    Button dropdown <span class="caret"></span>
  </a>
  <ul class="dropdown-menu" role="menu">
    <li><a href="#">Action</a></li>
    <li><a href="#">Another action</a></li>
    <li><a href="#">Something else here</a></li>
    <li class="divider"></li>
    <li><a href="#">Separated link</a></li>
  </ul>
</span>

This will allow the behavior you wanted - while still allowing clicking to show/hide the menu ;-)

However there's an annoyance: if you move the mouse cursor slower and pass the small gap between the toggle and menu, it will hide the menu.

So secondly, add a small CSS to remove the gap

.dropdown-menu {
    margin-top: 0;
}

See the action in this plunker.