How to control the HTML code of the main menu in WordPress

While constructing the Lava Lamp Menu, one of my first tasks was to add a designated class name to the theme main menu. It turned out to be a bigger challenge than I thought.

The menu on WordPress is constructed by the wp_nav_menu function located in the wp-includes/nav-menu-template.php file. It gets several parameters. By default this function will nest the menu’s unordered list within a ‘div’ tag, but that virtually can be controlled by passing a parameter ,and changed to either a ‘nav’ tag or nothing at all (in which case the menu is not wrapped within any html tag).

My goal was to get rid of the container tag, leave only the menu and define a css class to it.

So obvious thing to do is to call the menu function from within the them file (normally header.php) like so:
wp_nav_menu( array( 'container' => false, 'menu_class' => 'lavalamp', 'theme_location' => 'primary' ) );

All goes fine but one thing: the container tag remains, and my menu is indeed nested inside a div tag, that in its turn is dressed up with the css class originally meant for the actual menu ul still nested within it! I also realized that the problem occurred when instead of displaying the list of static pages in the blog, as would be the default case, the menu displays a customized list, constructed on the admin area using the ‘menus’ utility.

So I go further to explore the wp_nav_menu function. It appears that what eventually executes the menu’s HTML is a call-back function that by default is set to wp_page_menu (located in the wp-includes/post-template.php file), However, this function can be replaced, by passing another function’s name as a parameter value to the wp_nav_menu function. The parameter’s name is: fallback_cb

On this callback function couple of cases are handled:

  • Default WordPress settings, where the main menu only displays the blog static pages. In this case, there’s a check to see if and which should be the menu’s container
  • Custom menu (which is my case). Here, the check for container’s type or existence is neglected.

It is very easy to add this check to the default function. However, we risk at losing the fix next time WordPress upgrades. For this reason only, the best solution would be to create a new callback function and pass this function as a parameter instead. Here’s how it’s done:

  1. Locate the original default callback function (wp_page_menu located in wp-includes/post-template.php)
  2. Copy it to your functions.php file under your theme directory. If you don’t have such file yet, create a file called ‘functions.php’ and save it under your theme directory. Then, paste the copied function code in it.
  3. Change the function name to differ it from the original. This is crucial, because otherwise the page will stop loading with an exception for duplicated function names. Best would be to add the theme name as an infix like so: wp_mytheme_page_menu:
  4. In this function, replace the line
    if ( $menu )
    $menu = '<ul>' . $menu . '</ul>';
    $menu = '<div class="' . esc_attr($args['menu_class']) . '">' . $menu . "</div>n";

    if ( $menu )
    $menu = '<ul class="' . esc_attr($args['menu_class']) . '">' . $menu . '</ul>';
    if ($args['container']) { /* This check is missing in the original function. */
    $menu = '<div class="' . esc_attr($args['menu_class']) . '">' . $menu . "</div>n";
  5. Now call the menu function on your header like so:
    wp_nav_menu( array( 'container' => false, 'menu_class' => 'my_desired_css_class', 'theme_location' => 'primary','fallback_cb' => 'wp_mytheme_page_menu' ) );

Thus we achieve two things: First – we control which container wraps our menu, if at all. The second: which css class is assigned to the actual menu and not to its container.