Show top-level parent with submenu menu walker

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.

Read More

For example if the menu hierarchy is:

  • a
  • b
    • 1
      • i
      • ii

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";
        }
    }
}

Related posts

1 comment

  1. You can try to skip the following if-condition in the start_el() method in your child_menu_walker class:

    if ($item->menu_item_parent != $parent_item_id ) {
        //...
    }
    

    which is equal to:

    if ($item->menu_item_parent != 0 ) {
        //...
    }
    

    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:

    && $item->menu_item_parent != $parent_item_id
    

    in the end_el() method.

Comments are closed.