Improve your WordPress Navigation Menu Output
WordPress 3 has gone gold and ships with an amazing new menu manager that can be used to control the navigation menus of your website. This tutorial will teach you how to change the default output of this manager, since getting a custom output can heavily improve the style of your themes. So first of all here is an example of the wordpress menu we want to build.
How to display the content of the wordpress menu description field
As you can see, instead of a simple list we got the menu item name and below that name is a small description of that menu item. This is currently a rather popular style that unfortunatley can’t be done out of the box by wordpress.
As you may already know, once you have created a menu at your wordpress backend at Appearance > Menus you can use a wordpress function called wp_nav_menu() within your template files to display those menus.
The problem is, the basic output would look something like this:
<ul id="menu-main"> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Contact</a></li> <li><a href="#">Blog</a></li> </ul>
With a basic unordered list like that its almost impossible to create such a menu. So we need to change the output to get this:
<ul id="menu-main"> <li><a href="#"><strong>Home</strong><span>Starting the journey</span></a></li> <li><a href="#"><strong>About</strong><span>What to expect</span></a></li> <li><a href="#"><strong>Contact</strong><span>Get in touch!</span></a></li> <li><a href="#"><strong>Blog</strong><span> Latest storys</span></a></li> </ul>
The <strong> tags wrap arround the title whereas the description is put into the <span> tags. Those can be styled easily with CSS later on to create this special menu style.
Preparing the backend
The first thing we need to do is to setup the menu properly in our backend. WordPress already comes with the option to add a description to each menu item, but it is hidden by default.
When you are at the Appearance > Menus Site you need to look at the top right and you will notice a “Screen Option” tab. Click it and you will get the option to display several other input fields for each menu item, among them a checkbox to show the description.
Once that is done, if you start editing your items you will notice that you can now enter a description for each menu item.
By default wordpress adds a rather long description to menu items that are created by pages, I would recommend to just delete those enourmous novels and just add a few words just like I did.
Now that we have setup the data to display in our backend, its time to prepare the frontend to show that data.
Editing the output by using a custom walker
WordPress uses a special “Walker” class that iterates over each data record and then displays this record accordingly. The cool thing about that is that we can simply create our own custom walker extending that PHP class. That way we dont need to care about fetching the stuff from the database or preparing the data arrays. We only need to extend the part of the wordpress code that outputs the list. So open your functions.php file and add the following code:
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 ) .'"' : ''; $prepend = '<strong>'; $append = '</strong>'; $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 .$prepend.apply_filters( 'the_title', $item->title, $item->ID ).$append; $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 ); } }
So what does this Class do? This is basically the walker that wordpress is using (I just copied the source code) and added a few lines in the lower third for a better output: The walker now checks if a description is set, and if thats the case it wraps the description into a span tag and appends it to the navigation title.
The walker also checks if we are currently iterating over a sub menu item or a top level item, since our sub menu items do not need to display a description.
The Function Call
Now that we have created a custom walker we only need to tell wordpress that it should use our walker instead of its own. This can be easily done by calling the wp_nav_menu() with the walker parameter set:
wp_nav_menu( array( 'container' =>false, 'menu_class' => 'nav', 'echo' => true, 'before' => '', 'after' => '', 'link_before' => '', 'link_after' => '', 'depth' => 0, 'walker' => new description_walker()) );
Thats it. Once that is done your menu will be output with a completly different code structure that you can easily style with CSS to fit your needs. Here is a short snippet to get you startet:
.nav{ height:50px; padding-left:13px; margin:0; padding:0; list-style-type:none; list-style-position:outside; position:relative; } .nav a{ display:block; float:left; line-height:18px; outline:medium none; padding:2px 10px; text-decoration:none; width:95px; min-height: 35px; } .nav li a strong { display:block; font-size:14px; font-weight:normal; } .nav li a span { display:block; font-size:10px; line-height:14px; }
I hope you can utilize this knowledge to push the boundaries of beautiful wordpress generated menus ;)
Wow. Great tutorial kriesi. This will definitely come in handy to many wordpress developers and new learners. Looking forward to more great wordpress tutorials from you.
Amazing tuts, Big thanks to you friend, This can be a great help to create easy navigation with WordPress 3.0
been thinking about this one a couple of days ago. great trick. will be using it on a current project.
thanks for sharing this trick and your great timing :)
found an issue though: the output does not bring in the current_… classes anymore. can you confirm this?
They get applied as usual. the line that reads
$output .= $indent . ‘<li id="menu-item-‘. $item->ID . ‘"’ . $value . $class_names .’>’;
adds the classnames to the list, the following line adds the classes and attributes to the a tag
$item_output .= ‘<a’. $attributes .’>’;
Hi Christian. Nice tutorial. I’d suggest replacing:
$class_names = ”;
with:
$class_names = ‘ class=”‘ . esc_attr( $class_names ) . ‘”‘;
that should fix “class issues”.
Cheers Yuriy, Kriesi,
Yuriy’s suggestion solved the problem for me.
Now if only I would figure how to make it read [:en] or tags for language translated sites :) qtranslate is killing me.
Cezar
Ah, thanks for noticing. seems the visual editor just removed the code somwere during the copy/paste process :D
Already fixed it
This is the most useful WP 3.0 tutorial I’ve found yet. Keep it up! I’ll be using it this week on a Hot Rod Builder website I’m designing right now.
I’d love to see your ideas on how to use the Custom Post, Custom Category, Custom Index, & custom page options available in WP 3.0. I found and used some pretty cool WP 2.8 & 2.9 tutorials on Wpengineer’s website, but haven’t seen new tuts for WP 3.0 that have gone in depth at all.
I’ll definitely bookmark your site and look for more great tutorials to take WP to the next level of design!
Thanks Mark, really glad you like it ;)
Kriesi – The WP community is so fortunate to have you. You are constantly breaking new ground and helping out others. Thank you so much for all you do. Your work is amazing! – Scott
Here is a little fix if you have no description:
$desc = trim( $item->description );
$description = ! empty( $desc ) ? ''.esc_attr( $desc ).'' : '';
Otherwise you have a empty span, because WP save a empty description with a space.
It’s very cool! I was thinking about possibility to add such feature on one of my blogs, now I can do it =)
Awesome. Thanks for the tut!
Man I wish there was an easier way to do this. I’ve run into this problem so many times that I’ve stopped using wp_nav_menu() and resorted to hard-coding.
Thanks for this… gives us something to play with.
What’s wrong with adding:
<strong>Home</strong><span>Starting the journey</span>
as the Navigation Label on the menu item?
That works for me – it doesn’t seem to strip the HTML – and it’s a whole heap simpler than creating a custom walker to do the same thing…
If you sell themes, like I do, there is no way you can go that route. There are too many people who do not read instructions, dont know html, or will geneally mess it up some way :)
A rule of thumbs I’ve learned some time ago: never let a user enter chunks of html anywhere :)
Hi Kriesi,
Okay, I’m hearing you on that! :)
THANKS GOD pal, this is just was I was looking for, especially for the description within a span part.
You ended a long WordPress 3.0 nightmare somewhere in Sao Paulo, Brazil.
When I release my 1st theme you will surely be remembered.
Awsome, thanks.
I haven’t played around much with the new menu options yet. This should kick me off into the right direction!
could I add this entry? i will link back to this post.. please let me know, thanks
Hi Kriesi. Thanks for the guide above. Now on your main page [the page which contains the menu being built in this tutorial you have vertical drop-down effects. Can you please provide a guide as to how you’ve done this, so that other pages which should appear as sub-menu items will appear on my site/blog. Thanks ahead.
any thoughts on getting the “slug” name of the page for the menu?
i printed out the $item variable, but i don’t see that option available. $item->post_name doesn’t appear correct for Page type menu items.
for example, i have pages home, about, services, etc. i would like to class the with that name. basically:
$attributes .= ! empty( $item->post_name ) ? ‘ class=”nav_’ . esc_attr( $item->post_name ) .'”‘ : ”;
any help would be appreciated.
This is a good start to a tutorial, but it is not finished. If you say you want to design a menu to look like the one you have on https://kriesi.at/themes/avisio/ then you need to do a complete walk through of that process. Instead it is maybe the first 1/3 of what needs to be done.
It would be great if you could do a complete tutorial because there is not much out there for styling menus in wordpress 3.0 right now.
btw, since the post name didn’t see to pull correctly for the pages, i added the following code below the initial classes setup:
if ($item->object == “page”) {
$page_data = get_page( $item->object_id );
array_push($classes, $page_data->post_name);
}
Thanks Kriesi,
I wanted to remove the link from the parent menu item, and your code pointed me to right direction.
Kriesi,
This post was exactly what I was looking for, and the best description on what the Nav Walker is and how to modify it I have read. It should be directly linked from the WordPress Codex. Not to blow anymore sunshine up your back-side, but one really can see the passion you have for design by the care that is put into your posts. Again, Thank You!
-SwitzerBaden
[removed]
note by Kriesi: I will accept any constructive criticism that you offer, but an anonymous random flame in a comment wont do it ;)
Nice tutorial Kriesi! Thank You. Shall definitely be useful reference. Am always one for pushing the boundaries/limitations inherent in choosing WordPress and enjoying the freedom to improve menus. Keep ’em coming :-]
Excellent article. I found someone who was having an issue similar to mine in the wordpress forums and I recommended him to read this. Is well covered. Great work.
Hi, great tutorial.
I wonder if you could cover a topic on creating custom menu item, which could be added in Admin panel to custom menu?
Custom menu item could be for example a simple html panel I would like to insert into menu.
Thanks for this “only” tutorial about this menu in the internet. But i cant just get it work in 2010 theme. Just added the first code in functions.php. But
I dont understand the second step – function call. Where it belongs actually – in nav.menu.template – tried, messes whole page up. One way just warning, where the menu usually should be, other way didnt display nothing at all. Inserted function call in header.php – nothing happened.
I like this menu+description very much and hope to get it up soon…
Gert
how can I show menu description on the sub-menu when Im using them as a drop down?
and how can I show just child pages of specific parent page with description?
How to stop all the menu-item menu-item-type-post_type classes crap and just get it to output the current page indicator.