This is a hard question for me to ask, because I don't really know how to explain it. So, please, bear with me.
Bootstrap menu, as I know, has 2 modes: "desktop" mode "phone/tablet" mode. The desktop mode is anything above 767px width, and the later is anything below or equal (to 767px).
Easy, we copy the navbar example out of Bootstrap's site. And then we can see that the core of the menu looks something like this:
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
And something like this:
<ul class="nav navbar-nav">
<li class="active"><a href="#">Link</a></li>
<li><a href="#">Link</a></li>
</ul>
As we can see, the collapsing of the menu is set to the button that appears when the "phone/tablet" mode view is open, and that is fine, but the problem starts when we see that the menu doesn't collapse when we click on a link.
So I manage to solve it by adding the following attributes to each of the "a" elements:
data-toggle="collapse" data-target=".navbar-collapse"
Now it looks ugly(er) ... like this:
<ul class="nav navbar-nav">
<li class="active">
<a href="#" data-toggle="collapse" data-target=".navbar-collapse">Link</a>
</li>
<li>
<a href="#" data-toggle="collapse" data-target=".navbar-collapse">Link</a>
</li>
</ul>
But now, the problem is worst! When we click on a link and we are not on the "phone" view, we can see that the menu kind of collapses and then displays again.
So I see this solution as a workaround (not as a real fix), and to the eye of the developer (in this case.. me) very, very ugly.
So I solved it the next way:
With the project I'm working with, I have implemented the publish and subscribe pattern, which is not from this subject so I won't go into a dip explanation of it, but basically:
This will subscribe a function to a "topic name":
$.Topic(topicName).subscribe(function(){...});
And this will publish a topic to execute the functions(handlers/callbacks) that were subscribed to that topic:
$.Topic(topicName).publish(arguments);
That being said, the code I want to review is the following. Maybe there is already a better way, but this way has worked for me.
I first added an ID to the ul
element that holds the links to get an easy access, this way:
<ul id="navbar-links" class="nav navbar-nav"> ... </ul>
I subscribe to a topic called "resize":
$.Topic("resized").subscribe(function (){
var width = $(window).width(); // the current width of the window
if(width > 767/*pixels*/){
// Not a phone nor a tablet
$("#navbar-links a").attr("data-toggle", ""); // does not collapse
$("#navbar-links a").attr("data-target", ""); // does not even have a target to collapse
} else {
// The opposite
$("#navbar-links a").attr("data-toggle", "collapse");
$("#navbar-links a").attr("data-target", ".navbar-collapse");
}
});
What really does the trick is (thanks to jQuery and events of course):
$(window).on("resize", function(){
$.Topic("resized").publish();
});
$.Topic("resized").publish();
Let me know what you think of that code.
Just in case you are wondering for the implementation of the publish and subscribe:
var topics = {};
jQuery.Topic = function( id ) {
var callbacks, method,
topic = id && topics[ id ];
if ( !topic ) {
callbacks = jQuery.Callbacks();
topic = {
publish: callbacks.fire,
subscribe: callbacks.add,
unsubscribe: callbacks.remove
};
topics[ id ] = topic;
}
return topic;
};
Obviously it is encapsulated inside a module so the "topics" variable remains "private".