Folding Side Nav with jQuery

With jQuery, coding side navigation with drawers that expand and collapse is a piece of cake.

Let’s start with some best-practices HTML/CSS navigation:

<ul id="side-nav">
    <li class="current">
        <a href="/">Products</a>
        <ul>
            <li>
            <a href="/apples">Apples</a>
            </li>
            
            <li>
            <a href="/bananas">Bananas</a>
            </li>
        </ul>
    </li>
    
    <li>
        <a href="/">Services</a>
        <ul>
            <li>
            <a href="/juicing">Juicing</a>
            </li>
            
            <li>
            <a href="/canning">Canning</a>
            </li>
        </ul>
    </li>
    
    <li>
        <a href="/contact">Contact</a>
    </li>
</ul>

Notice that some of the top level items have children, while “Contact” does not. Now let’s add some CSS to ensure that all the navigation items are collapsed when the page loads, but let’s also make sure that we have a way to switch them back on:

#side-nav UL {
    display:none;
}

#side-nav UL.expanded {
    display:block;
}

Next, let’s use this CSS and jQuery’s click() function to not only expand and collapse the subnav items but also disable the link on the anchor tag:

$(function() {
    $('#side-nav LI').click(function(ev) {
        ev.preventDefault();
        $(this).children('UL').toggleClass('expanded');
    });
});

ev.preventDefault() prevents the default action of the anchor tag, which is to link to another page. But remember that some top level items don’t have a subnav, for these we will want to enable the link. Also we shouldn’t be running this function on all LI’s—we only need it at the top level. Even though this function will not throw an error on the lower level items, jQuery selectors are fairly resource heavy, and there is no need to run this here.

Let’s revise our jQuery and kill both of these birds with one stone:

$(function() {
    $('#side-nav LI:has(UL)').click(function(ev) {
        ev.preventDefault();
        $(this).children('UL').toggleClass('expanded');
    });
});

As explained by John Resig himself, the jQuery selector :has() operates almost identically to CSS3′s qualified selector: ‘#side-nav LI > UL’. This is a great way to ensure not only that this doesn’t run on any top level items without a subnav but also that it doesn’t run within any of the lower level items.

Finally, we’ll need to expand the currently selected subnav item on page load and there are basically two ways to do it: one that uses the back-end to serve different markup and another that uses only JavaScript / jQuery. The first, back-end method is often prefered, since it requires less JavaScript to be run on the user’s machine and the nav doesn’t flicker when the jQuery finally loads up and expands whichever nav item. But the strickly jQuery method also has its advantages: it uses more universal markup, so it requires less from the back-end.

Since this is a front-end tutorial, let’s use the front-end method, and add to our ready function:

$(function() {
    $('#side-nav LI.current UL').addClass('expanded');
    
    $('#side-nav LI:has(UL)').click(function(ev) {
        ev.preventDefault();
        $(this).children('UL').toggleClass('expanded');
    });
});

Now, when the page loads any currently selected nav item will expand.

(We’re reusing some code here, but it’s not too much, so let’s not be OO nerds and just call it a day :D )

Be Sociable, Share!

Tags: , , , ,

Leave a Comment