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 ;)

60 replies
Newer Comments »
  1. Kevinsturf
    Kevinsturf says:

    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.

  2. Cezar
    Cezar says:

    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 :)

    • Kriesi
      Kriesi says:

      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 .’>’;

    • Yuriy
      Yuriy says:

      Hi Christian. Nice tutorial. I’d suggest replacing:

      $class_names = ”;

      with:

      $class_names = ‘ class=”‘ . esc_attr( $class_names ) . ‘”‘;

      that should fix “class issues”.

    • Cezar
      Cezar says:

      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

    • Kriesi
      Kriesi says:

      Ah, thanks for noticing. seems the visual editor just removed the code somwere during the copy/paste process :D

      Already fixed it

  3. Mark
    Mark says:

    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!

  4. scott
    scott says:

    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

  5. Micha
    Micha says:

    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.

  6. Ted Goas
    Ted Goas says:

    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.

  7. Stephen Cronin
    Stephen Cronin says:

    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…

    • Kriesi
      Kriesi says:

      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 :)

  8. dedos.info
    dedos.info says:

    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.

  9. olanrewaju
    olanrewaju says:

    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.

  10. Matt
    Matt says:

    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.

  11. Dave
    Dave says:

    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.

  12. Matt
    Matt says:

    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);
    }

  13. Kai
    Kai says:

    Thanks Kriesi,
    I wanted to remove the link from the parent menu item, and your code pointed me to right direction.

  14. SwitzerBaden
    SwitzerBaden says:

    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

    • Craig
      Craig says:

      [removed]

      note by Kriesi: I will accept any constructive criticism that you offer, but an anonymous random flame in a comment wont do it ;)

  15. Harold
    Harold says:

    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 :-]

  16. Maciej Kuś
    Maciej Kuś says:

    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.

  17. gert
    gert says:

    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

  18. Ashif
    Ashif says:

    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?

  19. Robin
    Robin says:

    How to stop all the menu-item menu-item-type-post_type classes crap and just get it to output the current page indicator.

Trackbacks & Pingbacks

  1. […] 这个方法网上找到很多,不过最详细的还是这个:【传送门】,主要功能是在WordPress3.0的自定义目录功能中加入描述(description)内容,达到常见的 目录名+描述 的效果。 […]

  2. […] Improve your WordPress Navigation Menu Output […]

  3. […] Improve your WordPress Navigation Menu Output By Christian Budschedl, July 18th, 2010 Site: Kriesi […]

  4. […] Improve your WordPress Navigation Menu Output […]

Newer Comments »

Comments are closed.