Programming

WordPress: Creating Truly Custom Navigation with the Walker_Nav_Menu Class

2 minute read

This post is from August 2012. The screenshots show the interface as it was then; today it looks slightly different in places.

It often happens that a website navigation should display more than just the page name, including related information such as the description, the featured image, or anything else… To gain control over this, you need the Walker_Nav_Menu class.

Procedure
Enter the following code into your functions.php; it corresponds to WordPress’s own code. This is where the desired changes will then be implemented.

class Custom_Walker extends Walker_Nav_Menu{

  function start_el(&$output, $item, $depth, $args){
    
    $classes = empty ( $item->classes ) ? array () : (array) $item->classes;
    $class_names = join(" ", apply_filters("nav_menu_css_class", array_filter($classes), $item));

    ! empty ($class_names) and $class_names = " class='" . esc_attr($class_names) . "'";

    $output .= $indent . "

What do we want to change now? For example, instead of a <ul><li> navigation, we want simple <div> elements for the individual navigation items. Additionally, we want the featured image below it and the description (from the menu). It would look like this, for example:

// Add these lines in order to fetch the additional information you wish to display

// We need the target page id in order to fetch the page image
$page = get_page_by_path(basename($item->url));
$page_id = $page->ID;
// We should check if there is an image set or not, but we are lazy for this example
$image_url = get_the_post_thumbnail( $page_id, 'custom-image-preview' );
// Same thing for the description
$description = esc_attr( $item->description );

// Remove the li
// Instead of $item_output = $args->before . "" . $args->link_before . $title . " " . $args->link_after. $args->after;
$output .= "";

// Change the output like you need it
$item_output = $args->before . "
" . $args->link_before . $title . "
" . $image_url . "
" . $args->link_after. $args->after;

To ensure it is actually displayed, the wp_nav_menu call must be modified. For example, like this:

wp_nav_menu( array( 'theme_location' => 'my-custom-navigation', 
    'menu' => '', 
    'container' => 'nav', // Auto wrap navigation in <nav> area
    'container_class' => FALSE, 
    'container_id' => FALSE, 
    'menu_class' => FALSE, 
    'menu_id' => FALSE, 
    'echo' => true, 
    'fallback_cb' => 'wp_page_menu', 
    'before' => '', 
    'after' => '', 
    'link_before' => '', 
    'link_after' => '', 
    'items_wrap' => '%3$s', // Remove the ul container
    'depth' => 0, 
    'walker' => new Custom_Walker ) );