While I’m fond of Bootstrap in general, I dislike how much it relies on JavaScript and particularly dislike its dependency on jQuery. I have nothing against using JavaScript for creating interactive content, e.g. web applications, or progressive enhancement, e.g. lightboxes or copying text to the clipboard, but I do take issue with requiring JavaScript for basic site functionality as is the case with Bootstrap’s navbar and dropdowns. What I particularly dislike is that not only does this require JavaScript, but it requires a 32kB JavaScript library, jQuery, to do what would otherwise take less than 1kB of JavaScript. To avoid this when redesigning campworkcoeman.org, I made some modifications to open the navbar’s dropdowns on hover on larger devices and used a small piece of plain JavaScript to operate the menu on small, mobile devices. Unfortunately, this didn’t work properly under iOS, but I recently fixed this. I’m detailing the changes I made in case someone finds them useful.
As far as HTML goes, the only change is using <div>
elements instead of <a>
elements for the dropdown-toggle
class. In the CSS, a media query is used to open the dropdowns on hover on larger devices, and some code is added to restore the appearance of the dropdowns due to the HTML change.
/*
* Open dropdowns on hover instead of click.
*/
@media (min-width: 768px) {
.dropdown:hover {
background: #e7e7e7;
}
.dropdown:hover > .dropdown-menu {
display: block;
}
}
/*
* The following is needed since the dropdowns are <div>
* elements instead of <a> elements
*/
.nav > li > div {
position: relative;
display: block;
padding: 10px 15px;
cursor: default;
}
.navbar-nav > li > div {
padding-top: 15px;
padding-bottom: 15px;
line-height: 20px;
}
.navbar-default .navbar-nav > li > div {
color: rgb(119, 119, 119);
}
.navbar-collapse.collapse {
display: none;
}
.dropdown-open {
background: #e7e7e7;
}
Finally, a small script replaces the default Bootstrap bootstrap.js
version.
// Navbar and dropdowns
var toggle = document.getElementsByClassName('navbar-toggle')[0],
collapse = document.getElementsByClassName('navbar-collapse')[0],
dropdowns = document.getElementsByClassName('dropdown');;
// Toggle if navbar menu is open or closed
function toggleMenu() {
collapse.classList.toggle('collapse');
collapse.classList.toggle('in');
}
// Close all dropdown menus
function closeMenus() {
for (var j = 0; j < dropdowns.length; j++) {
dropdowns[j].getElementsByClassName('dropdown-toggle')[0].classList.remove('dropdown-open');
dropdowns[j].classList.remove('open');
}
}
// Add click handling to dropdowns
for (var i = 0; i < dropdowns.length; i++) {
dropdowns[i].addEventListener('click', function() {
if (document.body.clientWidth < 768) {
var open = this.classList.contains('open');
closeMenus();
if (!open) {
this.getElementsByClassName('dropdown-toggle')[0].classList.toggle('dropdown-open');
this.classList.toggle('open');
}
}
});
}
// Close dropdowns when screen becomes big enough to switch to open by hover
function closeMenusOnResize() {
if (document.body.clientWidth >= 768) {
closeMenus();
collapse.classList.add('collapse');
collapse.classList.remove('in');
}
}
// Event listeners
window.addEventListener('resize', closeMenusOnResize, false);
toggle.addEventListener('click', toggleMenu, false);
I modified Bootstrap’s navbar example to demonstrate this.
If you are looking to contribute to a broader project on this idea, feel free to join forces here
http://thednp.github.io/Native-Javascript-for-Bootstrap/
Thank you very much! This is what I was trying to do myself and couldn’t with my little expertise. You’re my hero! I’ll mention your name in comments.
Thanks a lot. This saved me a lot of time and headaches
Looks good. Can you make one for the new Bootstrap v4 as well?
Maybe once there’s a stable release. It also seems the developer behind Bootstrap Native has plans for Bootstrap 4.
This really helped me. Thank you so much for the code :) It saved my time.
Your bootstrap.js in es6 syntax.
const classChildren = x => document.getElementsByClassName(`navbar-${x}`)[0];
classChildren(‘toggle’).addEventListener(‘click’, () => {
classChildren(‘collapse’).classList.toggle(‘collapse’);
}, false);
All we do is binding the ‘hamburger’ button click action to toggle the dropdown menu.
Sadly, the dropdown cannot be opened by keyboard in your version…