Try Tuts+ Premium, Get Cash Back!
Function Examination: wp_nav_menu

Function Examination: wp_nav_menu

Tutorial Details
  • Program: WordPress
  • Difficulty: Moderate
  • Estimated Completion Time: 10-15 Minutes

When WordPress 3 presented us with the new Menus functionality, it changed the way we viewed navigation menus forever. No longer were we bound to using the normal page listing functions or building our own custom menu functions to integrate category and page menus as well as external or hard linked items within a nav menu. But just how custom can we get with this new functionality? In this tutorial, we’ll dive deep into everything that the wp_nav_menu function can do, use the Walker Class to add a sub description, and touch on some of its related functions.


The Parameters

The function has several parameters to work with. Here are the defaults as listed in the WordPress.org Codex:

<?php $defaults = array(
	'theme_location'  => ,
	'menu'            => ,
	'container'       => 'div',
	'container_class' => 'menu-{menu slug}-container',
	'container_id'    => ,
	'menu_class'      => 'menu',
	'menu_id'         => ,
	'echo'            => true,
	'fallback_cb'     => 'wp_page_menu',
	'before'          => ,
	'after'           => ,
	'link_before'     => ,
	'link_after'      => ,
	'items_wrap'      => '<ul id=\"%1$s\" class=\"%2$s\">%3$s</ul>',
	'depth'           => 0,
	'walker'          =>
);
?>

<?php wp_nav_menu( $defaults ); ?>

Theme Location

Using this parameter, we can set a theme location which is then used on the Menus page to set a menu to work in that part of your theme, without having to manually define which menu should appear there. This is very helpful for theme distributors because you’re able to use conditionals to display a menu only if the user has defined a menu for that location. The only other requirement is that you use the function register_nav_menu() to register those locations. This is usually done from your function files when you’re setting up support for menus.

Let’s start building our custom menu function parameters assuming that we’ve registered a theme location called "primary".

$params = array(
	'theme_location' => 'primary'
);

Menu

This parameter is used to manually define which menu should be used. In our example, we are only setting a generic menu location and not defining an exact one to use, but if we were wanting to tell the function to use a menu called "Primary Navigation", our parameters would look like this:

$params = array(
	'theme_location' => 'primary',
	'menu' => 'Primary Navigation'
);

Container

By default, our menu will be wrapped in a div, but if you’re like me, you usually don’t need this and probably want to cut back on the amount of divs and other tags being used to keep your code as tidy as possible. You could also use this parameter to define a different tag such as an html5 <section> or <nav>. For our example, we don’t want a container to change the default container values since the Twenty Eleven theme styles rely on it being there.

Container Class and Container ID

As you can pretty much guess, these parameters are used to set a class and an ID to the container. Since we’re omitting this altogether, we have no need to define values.

Menu Class and Menu ID

These work just like the previous parameters except this time we definitely want to set an ID of "nav" because that is the ID we’ll use in our stylesheet to style the navigation bar.

$params = array(
	'theme_location' => 'primary',
	'container' => false,
	'menu_id' => 'nav'
);

Echo

You can use this parameter to tell whether you want to display (echo) the results, or return it for use in PHP. This item is boolean so to return it simply set this parameter to 0.

Fallback CB

This is a callback function that you can fallback to if no menu is found. By default it uses the old stand by wp_page_menu() and passes all of the same parameters to this function as well.

Before and After

These items are used to define what can be placed before and after the anchor tags (<a></a>). You could use these to precede each item with a vertical bar, or wrap the nav items in a span tag.

Link Before and Link After

These work the same as the previous items we covered except that whatever you define will be inside of the anchor tags. Our example doesn’t require that we use these so we’ll ignore them and let the default empty item be.

Items Wrap

By default, the items are wrapped in an unordered list with the menu id and menu class. This parameter lets you change that if you so desire.

Depth

This parameter is really nice for when you want to use the same menu twice but don’t want any child items to display in the location you’re setting up with the wp_nav_menu() function. For instance, if you want the primary navigation to include a second level dropdown, you could leave this at the default setting. Then if you wanted to use the same parent items in a footer navigation and omit the child items, you could set this parameter to a depth of 1. The default "0" means all levels will be output. To keep our example concise, we’re assuming that the primary navigation doesn’t include any child items.

Walker

The parameter is used to define a walker object which can be used to manipulate how the entire function works and outputs its information. We’ll go over a good example in the next section.


Adding a Description to the Nav Menu Items

For our example, we want to add a sub description to each main menu item. The functionality to add the description itself is already in place in the WordPress Menu system. To turn this on, go to Menus and then press the screen options tab in the top right corner. The option you need to make sure is clicked should say, "Description". With this checked, a menu item should now look like this:

Once we have our descriptions filled out, we’ll need to create the walker class and add it to the wp_nav_menu() parameters. We’ll call the class description_navigation so our complete parameters code should look like this:

$params = array(
	'theme_location' => 'primary',
	'menu_id' => 'nav',
	'walker' => new description_walker()
);
wp_nav_menu($params);

The Walker Class

Now we’re ready to add those descriptions in using our Walker class:

class description_walker extends Walker_Nav_Menu {
	function start_el(&$output, $item, $depth, $args) {
		global $wp_query;
		$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

		$class_names = $value = '';

		$classes = empty( $item->classes ) ? array() : (array) $item->classes;

		$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
		$class_names = ' class="'. esc_attr( $class_names ) . '"';

		$output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';

		$attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
		$attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
		$attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
		$attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';
		$description  = ! empty( $item->description ) ? '<span>'.esc_attr( $item->description ).'</span>' : '';

		if($depth != 0) {
			$description = $append = $prepend = "";
		}

		$item_output = $args->before;
		$item_output .= '<a'. $attributes .'>';
		$item_output .= $args->link_before .apply_filters( 'the_title', $item->title, $item->ID );
		$item_output .= $description.$args->link_after;
		$item_output .= '</a>';
		$item_output .= $args->after;

		$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
	}
}

There is a lot going on here. For more information on Walker classes in general, let me refer you to another tutorial: Understanding the Walker Class. The most important part you should understand here is that we’re rebuilding the output of each link item and adding in the description. On line 19 of the snippet above you can see where we get the item description if it exists and make it the value of $description wrapped in a span tag so that we can style the descriptions separately. Then in lines 24-29 where we piece the link item back together, we add in the description right before the close of the anchor tag so that it becomes part of the link itself.

Using the Twenty Eleven theme, you should now have something that looks like this:

Style It Up

Let’s add a bit of styling to make it more legible:

#nav a {
	line-height: 20px;
	padding: 10px 15px;
}

#nav a span {
	display: block;
	font-size: 11px;
	color: #ccc;
}

#nav a:hover span {
	color: #999;
}

This will change the height and padding of each link, cause the description within the span tag to drop to its own line, and adjust the font sizes and colors a bit for a final result that looks like this:


Relation Functions

Not only can you use wp_nav_menu() to output your menu with all customizations, you can go a little further with some of its related functions.

has_nav_menu()

This function is great for only displaying a particular menu if that menu has been assigned to your theme location. For instance, you may want to create a top navigation on your theme for lesser navigation items that a user may not want in their main navigation. This could be things like a home link, "Advertise With Us", or other lower calls to action. But as a theme distributor, if you don’t know if that’s going to be something the user wants to use, simply use a condition like so:

if (has_nav_menu('top-menu')) {
	wp_nav_menu('theme_location='top-menu');
}

wp_get_nav_menu_items()

This function will return an array of items from a particular menu. This may be particular useful if you want to build a custom menu list without using a Walker Class. You lose a lot of functionality such as the menu item’s current class, but it’s a great way to loop through an array of menu items for a simple solution.


Conclusion

There are a lot of things you can do to customize your navigation menus when you know more about the flexibility that is offered with built in parameters and being able to have greater control with the Walker Class. Need to add another span tag with the class of "icon" for custom icons to each item? No problem.

Being able to have full control over the placement and output of menus extends your capabilities as a theme developer an unmeasurable amount of possibilities. What are some of the things you can use that Walker class to do?

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • Quirksmode

    Great read, thanks for this, the wp_nav_menu is a great feature for WordPress, though I still find it very restrictive.

    1) I would love the ability to automatically add sub items (pages, posts, categories etc. based on a dropdown choice) to parent items and not have to manually update each time.

    2) The sorting also seems quite time consuming, having to drag and drop the items one by one to get them into order. If there were options within the admin section to sort the order for each item that would be great.

    3) Sometimes I just want to show the subnav and not the whole nav. I have seen a few custom walkers to solve this, but I would love the WordPress devs to include it in the core as it’s so useful.

    A tutorial on building any/all of these features would be incredibly useful.

    • http://www.tammyhartdesigns.com Tammy Hart

      I agree that the drag and drop system seems to be a little clunky. I don’t know of the Trac tickets off the top of my head, but I’m fairly certain this is something they are working on.

      On point 3, are you referring to showing up to certain levels? you can do that with the depth parameter.

      • http://www.quirksmode.co.uk/ Quirksmode

        Good news on the drag and drop, fingers crossed they get something going for the next release.

        For point 3 I am referring to the ability to include a nav that starts at a certain depth. E.g. If I clicked on the main nav and went to the About page, I would like to include a second (sub) nav in a sidebar next to the main content. I would want this subnav to only feature the child pages of the parent page we are currently on.

        I managed to code a walker for this, but I hate having to stick all that code in the functions.php for something that to me should be part of the core as I reckon it’s a really useful feature people would use.

        I have been using WordPress for many years now and I have always felt that the implementation of navs and selected link highlighting never quite worked properly and was very fiddly to set up correctly. This very page is a good example of this where the url shows this post belongs to the Tutorials section and yet the Tutorials nav link at the top has no highlighting.

        Maybe I am just being way too fussy about this things :-p

    • http://jaredatchison.com Jared
  • Chris Janzen

    Fantatic tutorial at just the time I need it. How do you guys do it?

    • http://www.tammyhartdesigns.com Tammy Hart
      Author

      Just dumb luck, I guess. :) Can you share more on what you’re working on and how we helped?

      • Chris Janzen

        I didn’t see this comment until I was looking over the article again. I’m working on a new wordpress based website and want to build in Zurb Foundation functionality for the menu. Obviously I’ll need a custom walker to create the menu I wan to incorporate and this tutorial has helped me see how I can do just that.

  • mike

    The walker class has already been covered here recently. This is just a rehash of the same old stuff.

    • http://www.damiencarbery.com Damien Carbery

      I disagree that it is a rehash. Yes, the Walker class was discussed recently but not the wp_nav_menu options.

  • http://kg69design.com kg69design

    Excellent and useful tutorial. Many thanks.

  • http://www.amazing-web-design.co.uk/ Joe Elliott

    Thanks Tammy,

    Great post, sometimes you read the codex but I feel it’s just reference and you don’t really learn from it, learned loads now though.

    Joe :)

  • Kevin Doherty

    How would I edit the class to allow for the descriptions to render on sub menu elements as well and not just the top level?

    • Kevin Doherty

      If you Find


      if($depth != 0) {
      $description = $append = $prepend = "";
      }

      in the class and remove it, this allows for the description to render on all levels

      if you change it to:


      if($depth != 1) {
      $description = $append = $prepend = "";
      }

      this will only show the description on the 2nd level.

      very handy, thanks.

      • http://w3smart.com Tan Nguyen

        Change $depth != 1 to $depth < 2 :)

  • Fahad

    Hi, im integrating a web with wordpress,
    i dont know where but im getting wrong somewhere,
    i done navigation with
    ‘menu_order’,
    ‘title_li’ => ”,
    ‘depth’ => ’1′,
    ‘echo’ => 0
    );
    $separator = ”;
    $pattern = ‘/().*?().*?(<li)/is';
    $replace = '‘ . $separator . ‘

    but the class i made for active page is not working….

    Thanks for any help in advance…

  • Pingback: Tweet-Parade (no.22 May - June 2012) | gonzoblog.nl

  • http://www.scottfennell.com Scott Fennell

    Tammy,

    I want you to know that I find your tutorials very helpful. Any time I see “Tammy Hart” on something, I take the time to dig into it.

    Question though:

    Why am I seeing “emptyempty” throughout the code here:

    $classes = emptyempty( $item->classes ) ? array() : (array) $item->classes;

    • http://wp.envato.com/ Japh Thomson
      Staff

      Hey Scott, the “emptyempty” is a bug in our syntax highlighter. It should just be the empty() function. We have a fix on the way.

      Sorry for any confusion!

  • Dave Pitman

    Thanks for the tut.

    One thing that I can’t figure out how to do is add content within the opening anchor tag, not between the opening and closing tag.

    Why? in order to add data-toggle=”dropdown” for use with the Bootstrap framework.

    Seems quite important to be able to have the dropdown “click” open instead of just “hover” open because of mobile devices.

    Would love to here how to achieve this with WP’s menu.

    Thanks.

  • Pingback: WordPress Community Roundup for the Week Ending June 2

  • Flickapix

    Look at rachels wp fork of bootstrap on github its called wp bootsrap, in functions.php is the wp nav menu and walker class

    • Dave Pitman

      Thanks for the reply. I will check it out.

  • Pingback: Using Conditional Tags to Supercharge Your Blog | Wptuts+

  • Pingback: Using Conditional Tags to Supercharge Your Blog | Shadowtek Hosting and Design Solutions

  • Pingback: My Stream | Using Conditional Tags to Supercharge Your Blog | My Stream

  • Pingback: Using Conditional Tags to Supercharge Your Blog | How to Web

  • Ben

    Is there a way to tell him put an “value=”1″ to the current page’s
    so the current page will be ?

    Thank’s!

  • Ben

    *sorry for the last comment missing the code parts, here is the correct one:

    Is there a way to tell him put an “value=”1″ to the current page’s
    so the current page will be ?
    Thank’s!

  • Lance

    Any Idea how I could disable the top level nav elements.. ideally would
    just hover – to get sub-nav, then those would be click-able.

    • http://www.tammyhartdesigns.com Tammy Hart

      You could use jQuery and simply assign event.preventDefault() on click.

  • http://www.shovan.co.uk/ Shovan

    How to add inside ?

  • http://www.maverickcreative.ca/ Joshua Richards
    • http://www.tammyhartdesigns.com Tammy Hart

      Not a rip off, just a coincidence that we used the same methods to do the same thing! :)