3
\$\begingroup\$

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

\$\endgroup\$
3
\$\begingroup\$

Actually, there's sort of a built in way to do this. Just attach a click handler to the links in your .navbar-nav and call the collapse hide method on the navbar-collapse class:

$('.navbar-nav').on('click', 'li a', function() {
  $('.navbar-collapse').collapse('hide');
});

Oh yeah, and you don't have to worry about it when the nav isn't collapsed, so you don't have to listen for the resize event or anything.

\$\endgroup\$
3
\$\begingroup\$

The answer suggested by jmel1 caused problems for me. Everything is fine when the menu was in a "collapsible" state, i.e. on narrow pages. However, on wide pages, it seemed to cause glitches in the display of the un-collapsed menu.

Instead, I found this works well:

$('.navbar-collapse a').click('li', function() {
var navbar_toggle = $('.navbar-toggle');
if (navbar_toggle.is(':visible')) {
    navbar_toggle.trigger('click');
}});
\$\endgroup\$
  • \$\begingroup\$ Is this a correction of the OP code or of @jmel1 answer ? \$\endgroup\$ – Marc-Andre Jul 22 '14 at 14:57
  • \$\begingroup\$ An improvement (hopefully) of @jmel1's answer. \$\endgroup\$ – Russell Davies Jul 22 '14 at 15:23
  • \$\begingroup\$ The reason I'm asking this it's because an answer should be about the question code not another answer on the same question. But it's seems to me your code is not only directed at @jme11 answer, but is still "reviewing" op code. (it's jme11 not jmel1 ;) ) \$\endgroup\$ – Marc-Andre Jul 22 '14 at 15:29
  • 1
    \$\begingroup\$ To be clear, from the original question, "the problem starts when we see that the menu doesn't collapse when we click on a link." Jme11's code addresses that issue. However, it's not perfect, because it also introduces glitches at widths above the 767px breakpoint. My code addresses the same problem, in a manner similar to that of jme11, yet without the glitches at wider breakpoints. So, I've answered the original question/problem, whilst giving due credit to jme11 for his approach. So, what's the problem? \$\endgroup\$ – Russell Davies Jul 22 '14 at 15:47
  • \$\begingroup\$ There is no problem I just wanted to make sure ;) ! Sorry if I wasn't clear enough in my comment. I should have added " so this answer is not "off-topic"" at the end of my comment. \$\endgroup\$ – Marc-Andre Jul 22 '14 at 15:48
3
\$\begingroup\$

What I found the most "clean" solution is to use Bootstraps data-toggle, the trick is to add .in to data-target. So the link would look something like this

<li><a href="/help" data-toggle="collapse" data-target="#my_subnav.in">Help</a></li>

The in class is added to your collapsible navbar, when you expand it on smaller screens. So you can avoid the unwanted collapsing and showing again on larger viewports.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.