jQuery: using the DOM with navigation menus

The DOM: use it or leave it. In the special case of jQuery, using the DOM is made easier by jQuery's DOM traversal and manipulation methods. These methods, combined with the context provided by the selector chain, come handy when you have to deal with navigation menus.

Menus break down into two categories:

  1. linear menus (one level)
  2. nested menus (multiple levels)

The former case is pretty simple to handle. You can either choose to loop through all the navigation items:


// note #nav: the context
$('li', '#nav').each(function() {
	//...
});

or to target only the navigation links:


$('a', '#nav').on('click', function() {
	var $a = $(this); // this points to the current a element
	//...
});

When you're on a particular element within the menu, sometimes you need to apply some CSS styles via classes to other elements. The problem is that you're not always on the same DOM level of the other elements you want to select.

In the following example, your code works because all list items are on the same level in the DOM tree;


$('li', '#nav').each(function() {
	var $li = $(this);
	$li.click(function() {
		$li.addClass('test').siblings('li').removeClass('test');	
	});
});

On the contrary, when you target link elements you have to make some steps upwards in the DOM tree and use the outermost parent element:


$('a', '#nav').each(function() {
	var $a = $(this),
		$nav = $a.parents('#nav');
	$a.click(function() {
		$a.addClass('test');
		$nav.find('a').not($a).removeClass('test');	
	});
});

We've simply used the .not() filter to match all links except the current one. Notice how elements are placed on different levels of the DOM tree:


<ul id="nav"><!-- outermost parent -->
	<li><!-- first level -->
	  <a href="#">...</a><!-- second level -->
	</li>
	<!-- continues -->
</ul>

Nested menus are a little bit more complicated. This kind of menus adds one of more submenus within each list item:


<ul id="nav"><!-- outermost parent -->
	<li><!-- first level -->
	  <a href="#">...</a><!-- second level -->
	  <ul>
	  	<!-- submenu -->
	  </ul>
	</li>
	<!-- continues -->
</ul>

The first thing we have to do is to target the submenus. Generally, a commonly used practice is to mark up the list items which contain a submenu with a special CSS class so that it's easy to create actions with jQuery:


$('li.submenu', '#nav').each(function() {
	var $li = $(this),
		$submenu = $('a:first+ul', $li);
	//...


});

In this case we select only the very first submenu, namely the list subsequent to the first link. We don't know in advance how many menus are contained within each list item. This problem can be easily overcame by using the length property:


$('li.submenu', '#nav').each(function() {
	var $li = $(this),
		$submenu = $('ul', $li),
		len = $submenu.length;
	if(len == 1) {
		//...
	} else {
		//...
	}


});

If there's only one submenu, then we can perform a single action. If there are more submenus, we can loop through all the inner levels and attach an action to each submenu. This is useful also when you have to create a jQuery effect: a single submenu can be shown with a vertical sliding effect, whereas multiple menus can be shown with alternate effects.

As you can see, knowing the DOM is also knowing jQuery.

Back to top