I am a bit confused on the new bootstrap version since they changed dropdown menus to divs:
Neither of the top solutions worked for me.
This works perfectly, keeps submenus open while browsing, add uses the native Bootstrap javascript.
// Mouse over
$('body').on('mouseover', '.dropdown', function(e) {
$(this).children('.dropdown-toggle').dropdown('show');
});
// Mouse leave
$('body').on('mouseleave', '.dropdown', function(e) {
$(this).children('.dropdown-toggle').dropdown('hide');
});
I think this simply just work with bootstrap 4, i adding in inline but you always can bind event from script.
<a
onmouseover="$('#navbarDropdownMenuLink').dropdown('toggle')"
class="nav-link dropdown-toggle"
href="http://example.com"
id="navbarDropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
Dropdown link
</a>
$('body').on('mouseenter mouseleave','.dropdown',function(e){
var _d=$(e.target).closest('.dropdown');
if (e.type === 'mouseenter')_d.addClass('show');
setTimeout(function(){
_d.toggleClass('show', _d.is(':hover'));
$('[data-toggle="dropdown"]', _d).attr('aria-expanded',_d.is(':hover'));
},300);
});
/* this is not needed, just prevents page reload when a dd link is clicked */
$('.dropdown a').on('click tap', e => e.preventDefault())
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
<nav class="navbar navbar-toggleable-md navbar-light bg-faded">
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href>Navbar</a>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href>Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href>Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href>Pricing</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Dropdown link
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<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>
</li>
</ul>
</div>
</nav>
I had already used and styled a navbar when I was requested to change it to a hover interaction instead, so ended up with this as a fix using jQuery.
function bootstrapHoverMenu (bp = 768) {
// close all dropdowns that are open
$('body').click( function (e) {
$('.dropdown-menu.show').removeClass('show');
});
// show dropdown for the link clicked
$('.nav-item').hover(function (e) {
$('.dropdown-menu.show').removeClass('show');
if(( $(window).width() >= bp )) {
$dd = $(this).find('.dropdown-menu');
$dd.addClass('show');
}
});
// get href for top level link if clicked and open
$('.dropdown').click(function (e) {
if( $(window).width() < bp ) {
$('.dropdown-menu').css({'display': 'none'});
}
$href = $(this).find('.nav-link').attr('href');
window.open($href, '_self');
});
}
$(document).ready( function() {
// when page ready run the fix
bootstrapHoverMenu();
});
Downside is mobile only has top level links.
None of the CSS only answers work entirely. Either the dropdown menu stays open after click, or there is a gap that makes the dropdown menu hide before you can reach the menu links to click.
Here's the simple CSS only solution:
.navbar-nav li:hover .dropdown-menu {
display: block;
}
Remove data-toggle=dropdown
from the HTML markup to prevent the dropdown staying open in click. Use mt-0
(margin-top:0) to eliminate the gap above the menu, and make it possible to hover the menu items.
Demo https://www.codeply.com/go/awyU7VTIJf
Complete Code:
.navbar-nav li:hover .dropdown-menu {
display: block;
}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
..
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown">
Dropdown
</a>
<div class="dropdown-menu mt-0" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</li>
</ul>
</div>
</nav>
CSS solutions not working properly on touch device
I found that any CSS solutions made the menu stay open on touch devices, they didn't collapse anymore.
So I read the article: https://www.brianshim.com/webtricks/drop-down-menus-on-ios-and-android/ (by Brian Shim)
Very useful! It states that a touch device always first checks the existence of a hover class on an element.
But: by using jQuery .show() you introduce a style attribute (display:block;) that makes the menu open up on first touch. Now the menu has opened without the bootstrap 'show' class. If a user chooses a link from the dropdown menu it works perfectly. But if a user decides to close the menu without using it he has to tap twice to close the menu: At the first tap the original bootstrap 'show' class gets attached so the menu opens up again, at the second tap the menu closes due to normal bootstrap behaviour (removal of 'show' class).
To prevent this I used the article: https://codeburst.io/the-only-way-to-detect-touch-with-javascript-7791a3346685 (by David Gilbertson)
He has some very handy ways of detecting touch or hover devices.
So, combined the two authors with a bit jQuery of my own:
$(window).one('mouseover', function(){
window.USER_CAN_HOVER = true;
if(USER_CAN_HOVER){
jQuery('#navbarNavDropdown ul li.dropdown').on("mouseover", function() {
var $parent = jQuery(this);
var $dropdown = $parent.children('ul');
$dropdown.show(200,function() {
$parent.mouseleave(function() {
var $this = jQuery(this);
$this.children('ul').fadeOut(200);
});
});
});
};
}); Check once if a device allows a hover event. If it does, introduce the possibility to hover using .show(). If the device doesn't allow a hover event, the .show() never gets introduced so you get natural bootstrap behaviour on touch device.
Be sure to remove any CSS regarding menu hover classes.
Took me three days :) so I hope it helps some of you.