Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I tried looking for info about how to exclude/remove nav menu items from custom menus, and the only thread I found did not have any answers that were useful to me.

1. Background:

I put together a Dock menu using WP custom menus (wp_nav_menu) and jqDock on my site. Since jqDock needs continuous images or image links to work its magic, I' using a custom walker so the nav menu HTML output looks smth like this:

<div id="menu-first" class="nav">
<a><img src="http://path/to/image-1.png"/></a>
<a><img src="http://path/to/image-2.png"/></a>
<a><img src="http://path/to/image-3.png"/></a>
etc...
</div>

The code for my custom walker is:

class custom_nav_walker extends Walker_Nav_Menu 
{
    var $tree_type = array( 'post_type', 'taxonomy', 'custom' );
    var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );

    function start_lvl(&$output, $depth) {
        $indent = str_repeat("\t", $depth);
        $output .= "\n$indent<ul class=\"sub-menu\">\n";
    }

    function end_lvl(&$output, $depth) {
        $indent = str_repeat("\t", $depth);
        $output .= "$indent</ul>\n";
    }

    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;
        $classes[] = 'menu-item-' . $item->ID;

        $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
        $class_names = ' class="' . esc_attr( $class_names ) . '"';

        $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
        $id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';

        //$output .= $indent . '<li' . $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        ) .'"' : '';

        $description  = ! empty( $item->description ) ? esc_attr( strtolower( $item->description )) : '';
        $item_title   = ! empty( $item->attr_title )  ? esc_attr( $item->attr_title ) : '';

        if ( strpos($description, ';') !== false ) {
        $description_array = explode (';', $description);
            $image_name = $description_array[0];
            $image_alt = $description_array[1];
        } else {
            $image_name = $description;
            $image_alt = $item_title;
        }

        $item_output = $args->before;
        $item_output .= '<a'. $attributes .'>';
        $item_output .= $args->link_before .'<img src="'.get_bloginfo('template_url').'/images/skin1/'.$image_name.'" alt="'.$image_alt.'" title="'.$item_title.'" />'.$args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;

        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }

    function end_el(&$output, $item, $depth) {
        $output .= "";
    }

}

The jqDock script then catches the menu ID ('menu-first') and replaces the wp_nav_menu output with the Dock menu's. The Dock menu's HTML output changes based on the options specified when loading jqDock.

2. The Question:

I would like to not display (i.e., exclude) certain menu items according to where the user is on the site. For example, I would like to only show the Home item when the user is not in the Home, and the Random post item only when he is.

3. Discarded solutions:

a. Muliple menus: Registering and creating multiple menus and then calling them conditionally could work; however, I don't think this is an ideal nor a clean solution at all for many reasons. Also, multiple menus are not easy to maintain or update.

b. Regex Search and Replace: This might force me to change the needle parameter every time I change the jqDock options because the HTML output is modified.

c. CSS 'display' property: Hiding the items through the CSS display property works, but since it has to be applied to the jqDock menu output, it affects the visual rendering of the menu.

4. Failed Solutions:

*a. Filter to wp_nav_menu_items*: I tried catching the '$items' variable (string) and assigning it different values through conditional tags with the following code:

function userf_dynamic_nav_menu ($items) {
    $items_array_home = explode('<a', $items);
    $items_array_nothome = $items_array_home;

    unset($items_array_home[1]);
    unset($items_array_nothome[2]);

    $items_home = implode('<a', $items_array_home);
    $items_nothome = implode('<a', $items_array_nothome);

    if ( is_home() ) {
        $items = $items_home;
    } else {
        $items = $items_nothome;
    }
    return $items;
}
add_filter('wp_nav_menu_first_items', 'userf_dynamic_nav_menu');

This works only partially, because the menu items do change, but the conditional tags are ignored. I guess this makes sense because of the moment in which the filter is applied.

b. Custom nav menu function: I tried creating my own custom nav menu function to be able to add an exclude argument to the $defaults array and to use this slightly modified code from wp_list_pages to populate the additional argument:

$exclude_array = ( $args->exclude ) ? explode(',', $args->exclude) : array();
$args->exclude = implode( ',', apply_filters('wp_nav_menu_excludes', $exclude_array) );

Any ideas?

share|improve this question
    
Can you show us your custom walker child class? –  soulseekah Oct 21 '11 at 17:42
    
Hi Souleseekah, I just added it to my original post. Thanks! –  Marventus Oct 21 '11 at 17:52
    
I thought of passing an exclude argument too, but, contrary to wp_list_pages and many other WP functions, wp_nav_menu does not include one. So even if I do specify one when calling the menu or in the walker, it wouldn't get picked up inside wp_nav_menu, would it? –  Marventus Oct 21 '11 at 18:08
    
Sorry, wasn't thinking straight when I wrote that, deleted immediately. –  soulseekah Oct 21 '11 at 18:11
    
Don't worry about it! –  Marventus Oct 21 '11 at 18:16
show 4 more comments

2 Answers

up vote 5 down vote accepted

Method 1

You can add a constructor to your custom Walker to store some additional exclusion arguments, like:

class custom_nav_walker extends Walker_Nav_Menu {
    function __construct( $exclude = null ) {
        $this->exclude = $exclude;
    }

    function skip( $item ) {
        return in_array($item->ID, (array)$this->exclude);
        // or
        return in_array($item->title, (array)$this->exclude);
        // etc.
    }

    // ...inside start_el, end_el
    if ( $this->skip( $item ) ) return;
}

Or drop the constructor and set its $exclude property before passing it in as a walker to wp_nav_menu() like so:

$my_custom_nav_walker = new custom_nav_walker;
$my_custom_nav_walker->exclude = array( ... );

Depending on what you're excluding by, supply the correct form to the exclude.

Method 2

This is how you would go about doing this by hooking into the wp_get_nav_menu_items filter.

function wpse31748_exclude_menu_items( $items, $menu, $args ) {
    // Iterate over the items to search and destroy
    foreach ( $items as $key => $item ) {
        if ( $item->object_id == 168 ) unset( $items[$key] );
    }

    return $items;
}

add_filter( 'wp_get_nav_menu_items', 'wpse31748_exclude_menu_items', null, 3 );

Note: object_id is the object the menu points to, while ID is the menu ID, these are different.

Let me know your thoughts.

share|improve this answer
    
Thanks! That might work. I'll give it a try and let you know. –  Marventus Oct 21 '11 at 18:14
    
I tried the constuctor approach and, no matter what I try, I keep getting a "Wrong datatype for second argument" error for the in_array function. Am I doing something wrong? –  Marventus Oct 21 '11 at 18:41
    
The $exclude property must be an array. So make sure you're passing an array into the constructor, or watch the updated code in my answer. Specifically the typecast for $this->exclude, just in case an array is not passed. –  soulseekah Oct 21 '11 at 18:43
    
Sorry about that: I had a typo in my function. I just tried $exclude = array ('4', '7'); and using the slugs too, but it is not having any effects on the walker output. I'll try the second approach and let you know. –  Marventus Oct 21 '11 at 23:20
    
Nope, that didn't work either. I think I'm brain dead from trying to figure this out, so that might be affecting my... "performance", :-) –  Marventus Oct 21 '11 at 23:40
show 4 more comments

does this help

$exclude_array = ( $args->exclude ) ? explode(',', $args->exclude) : array();
$args->exclude = implode( ',', apply_filters('wp_nav_menu_excludes', $exclude_array) );

as an example

< ?php wp_nav_menu( array( 'container_class' => 'menu-header', 'theme_location' => 'primary', 'exclude' => '66' ) ); ?>
share|improve this answer
    
Hi Saq, I forgot to mention that one of the solutions that did not work was to create a custom nav_menu function and add that code as an additional argument to the function's defaults. Sadly, it did not work. I didn't try to include it in the walker, but I don't think that would work either for the same reason I mentioned above, mainly that the wp_nav_menu does not have an "exclude" argument, but I could be wrong. –  Marventus Oct 22 '11 at 1:35
    
I updated my original post to include this for clarity. –  Marventus Oct 22 '11 at 2:05
    
what if you dont use a custom walker, instead you use a regular nav_menu and extract the items with wp_get_nav_menu_items() with your custom image –  saq Oct 22 '11 at 3:45
    
That would be a good workaround in general, but in this particular case, wp_get_nav_menu_items won't retrieve the images because the img tags are not stored in the actual custom menu (only their filenames are in the description field, e.g., "image1.png"). The custom walker is what allows me to insert the img tags in the menu output. –  Marventus Oct 22 '11 at 11:53
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.