20

I'm familiar with WordPress and using the WordPress menu system. But I'm looking for a way to add custom HTML to wp_nav_menu().

I'm trying to create a menu like this: enter image description here

Notice how the drop down menu under products contains an image and a link. I'd like to re-create this. I've looked at a few plugins, but would rather code it.

I don't mind hard coding the image and link, but I'd like to keep the flexibility of using WordPress to manage the menus.

Kirk Beard
  • 9,569
  • 12
  • 43
  • 47
StephenMeehan
  • 1,083
  • 2
  • 15
  • 26

1 Answers1

61

The way WordPress goes through the menu pages to display the items, is using a walker object. In this case the specific class for this object is called Walker_Nav_Menu. You can find it in wp-includes\nav-menu-template.php.

The Walker_Nav_Menu is a pretty simple class. You are able to see, how the links and the menu structure are built there. The functions start_el and end_el are used to build the menu-items. Functions start_lvl and end_lvl are for nesting menus. In this approach we'll be mainly using start_el and end_el.

In your functions.php create a class, to extend Walker_Nav_Menu with pretty similar methods to the parent class:

class Custom_Walker_Nav_Menu extends Walker_Nav_Menu {
  function start_el ( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    // Copy all the start_el code from source, and modify
  }

  function end_el( &$output, $item, $depth = 0, $args = array() ) {
    // Copy all the end_el code from source, and modify
  }
}

In those functions, the $item is your menu-item, with which you can query additional contents according to the current menu-item, if you want to. Note that I didn't include start_lvl and end_lvl, but that doesn't matter, since your class will automatically inherit the parent classes methods, if not overwritten.

Then, in your theme files, you can call wp_nav_menu like this:

wp_nav_menu(array(
  'theme_location' => 'main',
  'container' => false,
  'menu_id' => 'nav',
  'depth' => 1,
  // This one is the important part:
  'walker' => new Custom_Walker_Nav_Menu
));

WordPress will use your custom class and functions, so that you can modify what code is output.

martinczerwi
  • 2,837
  • 23
  • 23
  • Thanks Martin, I'll take a look at this in some detail this evening. I guess I add in my custom HTML in the start_el? – StephenMeehan Sep 04 '12 at 07:49
  • Yeah right. You could as well add an opening html tag in the start_el, and close it in the end_el, but you can keep it simple ;) – martinczerwi Sep 04 '12 at 10:35
  • Does it matter what part of functions.php to place the Class? For instance, there are several functions, including setup functions there. – Daniel Dropik Jan 13 '14 at 17:51
  • For a clean coding style, you should put it in a separate file, and [include](http://php.net/manual/en/function.include.php)/[require](http://php.net/manual/en/function.require.php) it at the top of functions.php. You can also place it at the top, if you wanted to. I don't think you should place it inside of a function, but if you want to, make sure the function is called before `wp_nav_menu()` – martinczerwi Jan 19 '14 at 18:25
  • Don't copy the content of the extended class function! Use `parent::end_el($output, $item, $depth, $args);` – Armel Larcier Jan 29 '15 at 15:26
  • @ArmelLarcier How can you add custom HTML with just calling? Please provide a better answer, you'll surely get voted up. – martinczerwi Feb 02 '15 at 12:27
  • @martinczerwi Sorry what? Please rephrase... My comment is just about calling the parent class' method without rewriting it. – Armel Larcier Feb 02 '15 at 13:17
  • @ArmelLarcier You're saying "Don't copy", but the question is about editing the output HTML, and that's why we're copying, i.e. to modify the tags/text appended to the output string. If you just want the function called, you don't need to define that method at all, as inheritance will handle that. But I assume the reader might know that. So there's no need for `parent::end_el(...)`. – martinczerwi Feb 02 '15 at 23:21
  • "// Copy all the end_el code from source, and modify" Is no use, you can just modify the values of the arguments and call the parent function afterwards to parse output with you previous modifications included. – Armel Larcier Feb 03 '15 at 11:54
  • Have you looked at the parent functions (start_el and end_el) in detail? Imagine if you were to build the structure from the picture in the question. You'd need some more custom markup, and I think it's better to build it right into your class, instead of editing the string afterwards and inserting some markup. This is less error prone. If I'd pick your approach, I would use the filter anyway. No need for a custom walker. But more complex solutions require this approach. – martinczerwi Feb 04 '15 at 11:28
  • 2
    Is there a means to put custom code between a particular li? – Neil May 06 '15 at 14:07
  • Do I need a custom walker class if I want a simple div after the a tag in each list item ? – landed Sep 09 '16 at 13:37
  • 4
    Class is now called class-walker-nav-menu.php otherwise it worked like a charm. Quite a few lines of code to copy though but its working and it was easy to follow thank you. – landed Sep 09 '16 at 14:16
  • Thank's dear, Big like – Milad Ghiravani Aug 31 '17 at 19:10