I’ve also written a Custom Comment Walker function, after 3 hours of searching, I’m convinced it may be the only one out there lol…

If you’re trying to make a fancy navigation menu with CSS, you might have found the default IDs and classes applied by WordPress to the nav menu elements to be quite lacking. However, you can fix that by adding a custom walker function to add your own conditional classes. The custom nav walker function I made below will add the following CSS classes:

CSS Classes

  • .main-menu to top menu
  • .sub-menu to menus inside the main-menu
  • .menu-item to all <li>’s
  • .main-menu-item to all <li>’s in the main menu
  • .sub-menu-item to all <li>’s in a sub-menu
  • .sub-sub-menu to all menus inside a sub-menu
  • .menu-even/.menu-odd for <ul>’s (alternating)
  • .menu-depth-# to tell you how deep each menu is
  • .menu-item-even or .menu-item-odd (alternating)
  • .menu-item-depth-# to tell you which level menu the <li> is in
  • .menu-link to links

Optional Features

These requested features were can also be added to the code below. Click on each to be taken to the comment on this page describing how to integrate it.

This should make styling your Navigation Menus MUCH simpler.

Code

You call the function by supplying the name in the $args when you call the wp_nav_menu function in you theme. So for example, to enable our walker, we would call the menu with this:

Print Registered Nav Menu

wp_nav_menu( array(
    'theme_location'    => 'navigation_menu_primary',
    'container'     => 'div',
    'container_id'      => 'top-navigation-primary',
    'conatiner_class'   => 'top-navigation',
    'menu_class'        => 'menu main-menu menu-depth-0 menu-even', 
    'echo'          => true,
    'items_wrap'        => '<ul id="%1$s" class="%2$s">%3$s</ul>',
    'depth'         => 10, 
    'walker'        => new themeslug_walker_nav_menu
) ); // thanks nick

This would create the nav menu for the location “navigation_menu_secondary” give it a div wrapper with an id and class, and the first level menu will get the added classes main-menu, menu-depth-0, and menu-even. Now on to our custom walker function:

Custom Nav Menu Walker Function

class themeslug_walker_nav_menu extends Walker_Nav_Menu {
 
// add classes to ul sub-menus
function start_lvl( &$output, $depth ) {
    // depth dependent classes
    $indent = ( $depth > 0  ? str_repeat( "\t", $depth ) : '' ); // code indent
    $display_depth = ( $depth + 1); // because it counts the first submenu as 0
    $classes = array(
        'sub-menu',
        ( $display_depth % 2  ? 'menu-odd' : 'menu-even' ),
        ( $display_depth >=2 ? 'sub-sub-menu' : '' ),
        'menu-depth-' . $display_depth
        );
    $class_names = implode( ' ', $classes );
 
    // build html
    $output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
}
 
// add main/sub classes to li's and links
 function start_el( &$output, $item, $depth, $args ) {
    global $wp_query;
    $indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
 
    // depth dependent classes
    $depth_classes = array(
        ( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
        ( $depth >=2 ? 'sub-sub-menu-item' : '' ),
        ( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
        'menu-item-depth-' . $depth
    );
    $depth_class_names = esc_attr( implode( ' ', $depth_classes ) );
 
    // passed classes
    $classes = empty( $item->classes ) ? array() : (array) $item->classes;
    $class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );
 
    // build html
    $output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';
 
    // link attributes
    $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        ) .'"' : '';
    $attributes .= ' class="menu-link ' . ( $depth > 0 ? 'sub-menu-link' : 'main-menu-link' ) . '"';
 
    $item_output = sprintf( '%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s',
        $args->before,
        $attributes,
        $args->link_before,
        apply_filters( 'the_title', $item->title, $item->ID ),
        $args->link_after,
        $args->after
    );
 
    // build html
    $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}

I hope this was helpful! If you have an improvement of this code, let me know and I’ll incorporate your changes (with credit, of course).

29 Thoughts on “Custom Nav Menu Walker Function

  1. Nick Nasty on September 26, 2012 at 7:20 am said:

    You have syntax error in first block of code. You are closing the array but not the function.

  2. Alejandro on October 24, 2012 at 10:05 pm said:

    Hello, thanks for sharing this code snippet.

    I am wondering if it’s possible to exclude one menu item based on its ID

    Thanks in advance.

    • bitacre on October 25, 2012 at 5:30 pm said:

      Sure, on the second line of the start_el() function (on the line right after global $wp_query;) insert the following code to skip a particular menu item:

      $id_to_skip = 44; 
      if( $item->ID == $id_to_skip ) 
          return $output;
      

      If you want to skip multiple IDs, use this slightly more complicated version:

      $ids_to_skip = array( 44, 45, 46 ); 
      foreach( $ids_to_skip as $id_to_skip )
          if( $item->ID == $id_to_skip ) 
              return $output;
      

      Hope this helps!

  3. Alejandro on October 25, 2012 at 9:39 pm said:

    Thanks bitacre, works like a charm!

    I was wondering if there some way to pass the ID through the wp_nav_menu array.

    Thanks again!

  4. I’m trying to figure out the best solution to display the featured image (as a background) of each post, if the post doesn’t have a feature image it will set a default one. I can see it achievable using the walkers, any hints from where I should start?

    Thanks

    • bitacre on November 10, 2012 at 3:05 pm said:

      I can definetly give you a place to start:

      Anywhere in the start_el() function, you can determine if the current menu item has an associated thumbnail by using $bool = has_post_thumbnail( $item->ID);.

      That will return a boolean which you can craft into an if() statement to display the post thumbnail. I can’t get any more specific than that without knowing exactly how you want it displayed, but I’m sure you can figure it out using the post thumbnail functions, and item->ID to specify the post’s ID.

      Let me know if you get stuck and what you’re solution is, I may add this to the list of advanced options on this tutorial! Thanks!

      • Powla on May 2, 2013 at 1:54 am said:

        Can you use the above-mentioned to add post thumbnails to each of the sub-menu

      • items ?
  • A couple Questions:

    I’m wanting to add classes to the sub-menus. this seems like what I need…

    Is the Custom Nav Menu Walker Function supposed to be copied into the nav-menu-template.php?

    • bitacre on December 5, 2012 at 5:47 pm said:

      It’s a function, so it belongs in functions.php or in whatever sub-file is appropriate if you break down your functions into several different files and include them in functions.php. If you put it anywhere else, there’s no guarantee that template file will be loaded.

  • Hi,

    I’m trying to modify this with a function end_lvl to add a div to the end of the ul. The problem is that the div needs to contain unique content in each dropdown, so I’m trying to use $item->ID to find it! Here is my code:

    // add div to sub-menu function end_lvl( &$output, $item, $depth ) { global $wp_query; $indent = str_repeat(“\t”, $depth);

        if ($item->ID==37) {
            $output .= "$indent<div class=\"after-menu\">Stuff</div>\n</ul>\n";
        } else {
            $output .= "$indent</ul>\n";
        }       
    

    }

    I can’t seem to use $item or the global query to add the item ID at any point. Do you have any suggestions?

    • Sorry the code didn’t paste very well:

      // add div to sub-menu
      function end_lvl( &$output, $item, $depth ) {
           global $wp_query;
          $indent = str_repeat("\t", $depth);
      
          if ($item->ID==37) {
              $output .= "$indent<div class=\"after-menu\">Stuff</div>\n</ul>\n";
          } else {
              $output .= "$indent</ul>\n";
          }       
      

      }

  • How I can add a class only to the top level parent?

  • Pingback: Navigation Menus | Anand Verma

  • thanks dude.. :D your snippet code make my life easier lol

  • hi bitacre,

    I am trying to add a class to the parent li the above code by samuel is adding the to second level li.

    And another thing is that how do I add an extra li to ad the seperator.

    Looking farward for your earliest response.

    Imran

  • Pingback: Custom nav menu walker function « Wiki

  • Glitch on May 8, 2013 at 6:31 am said:

    Nice post. I would like to see one addition but I’m not sure if it’s possible at all. I would like to add a class to all

  • ‘s that contain a sub-sub-menu. Can it be done?
  • Pingback: wordpress wp_nav_menu()函数详解-wordpress

  • Pingback: » LOL +1

  • shishir umrao on August 3, 2013 at 2:17 am said:

    How to change a class of an item who has a sub-item ?

  • Pingback: How to add Class to <li> using wp_nav_menu() in Wordpress | Don't Be THAT Dev

  • As i try to get our Website work with wordpress I have a problem with our menu. I want to get the UL count with this code :

    function start_lvl( &$output, $depth = 0, $args = array() ) { $indent = str_repeat(“\t”, $depth); $output .= “\n$indent

    <

    ul class=\”level-”.$depth.”\”>\n”; } at nav-menu-template.php and it works very well ! But unfortunately it starts again with the number 0 everytime another first level UL begins. I want to give each first level UL his own background, but with this solution they all have class level-0. Do you maybe have any ideas ? Thanky a lot You cann see the quellcode at http://www.finanzfavorit.de/neu

  • Pingback: How do I generate a custom menu/sub-menu system using wp_get_nav_menu_items in wordpress? - PHP Solutions - Developers Q & A

  • daniel on May 1, 2014 at 1:53 pm said:

    Hey I’m trying to use this walker code to add aria functionality. I was wondering if someone could help me figure out how to access data from the $item variable inside of the start_lvl function? Basically I’m not that great with PHP at the moment, so I don’t fully understand how the code is being executed, but with Aria menus the base link/a tag at depth of 0 needs to have an ID which I’m using the title of the item, that piece works in adding it to the base link/a tag, as well as all the other links/a tags, which is okay. I now need to get the attribute aria-labelledby to mirror the $item->title or ID of the a tag. Any help would be much appreciated.

    I’ve added this code to my start_el function: $item_name = esc_attr( $item->title ); $attributes .= ! empty( $item->ID ) ? ‘ id=”‘ . esc_attr( $item->title ) .’”‘ : ”;

    And I’ve added this code to my start_lvl function: $output .= “\n” . $indent . ‘

    <

    ul role=”menu” aria-labelledby=”‘ . $item_name . ‘” class=”‘ . $class_names . ‘”>’ . “\n”;

  • Thank you for this incredible resource. I’m trying to parse the information and have a question about assigning individual classes to the menu’s

  • ‘s. I tried going through the Menu > Advanced > Custom CSS portion in the WP backend, but they aren’t appearing in my menu items…just the default “page_item page-item-12 page_item_has_children” etc.
  • Would you have a way to assign specific classes to each menu item? I’m also wondering how I could insert custom HTML and an image into one of the

  • ‘s.
  • Thanks for any help!

    Reply
  • Hey there, I know this post is a little old but I can’t seem to find any help for what I’m trying to accomplish.

    Basically, I’m trying to add schema.org language to my Nav Menu and its items.

    I basically need to wrap my element with and my

  • ‘s with
  • Any help would be much appreciated, I can’t seem to find any snippets online for this simple modification with the Walker_Nav_Menu

    Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

    Post Navigation