I’ve used the menu walker below to display dynamic submenus with great effect in the past.
It displays 2nd- or lower-level menu items IF the current page is a parent, sibling, or descendant of the page.
For example if the menu hierarchy is:
- a
- b
- 1
- i
- ii
- 1
It will show, 1, i, and ii if on pages B, 1, i, or ii. What I want to do is add the parent item (B in the example) to that so that the top-level parent also appears.
I’ve read up on walkers, tried to make sense of this code, but am still stuck even on where to begin.
// Show only the child pages of a menu
class child_menu_walker extends Walker_Nav_Menu {
var $found_parents = array();
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
if( $wp_query->is_single && !in_array( get_option('page_for_posts'), $this->found_parents ) ) {
$this->found_parents[] = get_option('page_for_posts');
}
//this only works for second level sub navigations
$parent_item_id = 0;
$indent = ($depth) ? str_repeat("t", $depth) : '';
$class_names = '';
$classes = empty($item->classes) ? array() : (array) $item->classes;
$class_names = join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item));
$class_names = ' class="'.esc_attr($class_names).'"';
#current_page_item
// Checks if the current element is in the current selection
if(
strpos($class_names, 'current-menu-item') ||
strpos($class_names, 'current-menu-parent') ||
strpos($class_names, 'current-menu-ancestor') ||
( is_array($this->found_parents) &&
in_array($item->menu_item_parent, $this->found_parents) )
) {
// Keep track of all selected parents
$this->found_parents[] = $item->ID;
//check if the item_parent matches the current item_parent
$item_output = '';
if ($item->menu_item_parent != $parent_item_id ) {
$output .= $indent.'<li'.$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).'"' : '';
$item_output = $args->before;
$item_output .= '<a'.$attributes.'>';
$item_output .= $args->link_before.apply_filters('the_title', $item->title, $item->ID).$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) {
$parent_item_id = 0;
$class_names = '';
$classes = empty($item->classes) ? array() : (array) $item->classes;
$class_names = join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item));
$class_names = ' class="'.esc_attr($class_names).'"';
if(
strpos($class_names, 'current-menu-item') ||
strpos($class_names, 'current-menu-parent') ||
strpos($class_names, 'current-menu-ancestor') ||
(is_array($this->found_parents) &&
in_array($item->menu_item_parent, $this->found_parents))
) {
// Closes only the opened li
if (is_array($this->found_parents) && in_array($item->ID, $this->found_parents) && $item->menu_item_parent != $parent_item_id) {
$output .= "</li>n";
}
}
}
function end_lvl(&$output, $depth) {
$indent = str_repeat("t", $depth);
// If the sub-menu is empty, strip the opening tag, else closes it
if (substr($output, -22) == "<ul class="sub-menu">n") {
$output = substr($output, 0, strlen($output) - 23);
} else {
$output .= "$indent</ul>n";
}
}
}
You can try to skip the following
if
-condition in thestart_el()
method in yourchild_menu_walker
class:which is equal to:
since you have
$parent_item_id = 0
.So it looks like this condition is filtering out your top-level parent.
You might also want to reconsider this condition:
in the
end_el()
method.