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?
Method 1
You can add a constructor to your custom Walker to store some additional exclusion arguments, like:
Or drop the constructor and set its
$exclude
property before passing it in as a walker towp_nav_menu()
like so: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.Note:
object_id
is the object the menu points to, whileID
is the menu ID, these are different.Let me know your thoughts.
does this help
as an example