6

I've just implemented the technique described at http://wordpress.org/support/topic/wp_nav_menu-list-only-2nd-level-separate-submenu/page/2 when implementing a theme for a client. When I run the theme locally on my Wordpress stack it behaves as expected displaying only the children of the parent menu item. When I deploy this to the server it displays all menu items including parents and children which obviously isn't the desired result. My code is as follows:-

From functions.php:-

class Custom_Walker_Nav_Sub_Menu extends Walker_Nav_Menu {

  var $found_parents = array();

function start_el(&$output, $item, $depth, $args) {

        global $wp_query;

        //this only works for second level sub navigations
        $parent_item_id = 0;

        $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

        $class_names = $value = '';

        $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 ( ($item->menu_item_parent==0) && (strpos($class_names, 'current-menu-parent')) ) { 
         $output.= '
<li>';
         }


        // 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
            if($item->menu_item_parent!=$parent_item_id){

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

                $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) {
      // Closes only the opened li
      if ( is_array($this->found_parents) && in_array( $item->ID, $this->found_parents ) ) {
          $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";
    }
  }

}

From the template:-

<!-- start navigation -->
<div id="nav">
<div id="page-navigation">

    <?php wp_nav_menu( array('theme_location' => 'primary', 'menu_class' => 'nav-menu', 'container' => '', 'depth' => 1,  )); ?>
</div>  
<div id="subnav">
    <?php
    $menu_args = array(
  'walker'          => new Custom_Walker_Nav_Sub_Menu(),
  'container'       => '',
  'menu_class'      => 'sister-pages',
s);
wp_nav_menu($menu_args);
?>
</div>
</div>
<!-- end navigation -->

The generated source:-

<!-- start navigation -->
<div id="nav">
<div id="page-navigation">
<ul id="menu-main-menu" class="nav-menu">
<li id="menu-item-6931" class="menu-item menu-item-type-custom menu-item-object-custom current-menu-item current_page_item menu-item-home">
<a href="http://www.landlordreferencing.co.uk/">Our Products</a>
</li>
<li id="menu-item-6891" class="menu-item menu-item-type-custom menu-item-object-custom">
<a href="/wp-login.php?redirect_to=%2Fexisting-members%2F">Existing Members</a>
</li>
<li id="menu-item-6884" class="menu-item menu-item-type-custom menu-item-object-custom">
<a href="/business-directory/">Business Directory</a>
</li>
</ul>
</div>
<div id="subnav">
<ul id="menu-main-menu-1" class="sister-pages">
<li id="menu-item-6931" class="menu-item menu-item-type-custom menu-item-object-custom current-menu-item current_page_item menu-item-home">
<a href="http://www.landlordreferencing.co.uk/">Our Products</a>
<ul class="sub-menu">
<li style="display:none;"></li>
<li id="menu-item-6886" class="menu-item menu-item-type-post_type menu-item-object-page">
<a href="http://www.landlordreferencing.co.uk/home-2/">Overview</a>
</li>
<li id="menu-item-6888" class="menu-item menu-item-type-post_type menu-item-object-page">
<a href="http://www.landlordreferencing.co.uk/tenant-lifestyle-referencing/">Tenant Lifestyle Referencing</a>
</li>
<li id="menu-item-6920" class="menu-item menu-item-type-post_type menu-item-object-page">
<a href="http://www.landlordreferencing.co.uk/tenant-credit-referencing/">Tenant Credit Referencing</a>
</li>
<li id="menu-item-6889" class="menu-item menu-item-type-post_type menu-item-object-page">
<a href="http://www.landlordreferencing.co.uk/insurance/">Insurance</a>
</li>
</ul>
</li>
<li id="menu-item-6891" class="menu-item menu-item-type-custom menu-item-object-custom">
<a href="/wp-login.php?redirect_to=%2Fexisting-members%2F">Existing Members</a>
<ul class="sub-menu">
<li style="display:none;"></li>
<li id="menu-item-6912" class="menu-item menu-item-type-post_type menu-item-object-page">
<a href="http://www.landlordreferencing.co.uk/register-a-tenant/">Register a Tenant</a>
</li>
<li id="menu-item-6925" class="menu-item menu-item-type-post_type menu-item-object-page">
<a href="http://www.landlordreferencing.co.uk/tenant-search/">Tenant Lifestyle Search</a>
</li>
<li id="menu-item-6903" class="menu-item menu-item-type-post_type menu-item-object-page">
<a href="http://www.landlordreferencing.co.uk/access-experian/">Tenant Credit Search</a>
</li>
<li id="menu-item-6930" class="menu-item menu-item-type-custom menu-item-object-custom">
<a href="/questions/">Discuss</a>
</li>
</ul>
</li>
<li id="menu-item-6884" class="menu-item menu-item-type-custom menu-item-object-custom">
<a href="/business-directory/">Business Directory</a>
</li>
</ul>
</div>
</div>
<!-- end navigation -->

1 Answer 1

5

Here is a much simpler implementation:

class UL_Submenu_Walker extends Walker_Nav_Menu {
    private $hidden = false;

    function start_lvl(&$output, $depth) {
        if($depth == 0) {
            $style = $this->hidden ? "" : "display:none;";
        }
        $output .= "<ul class=\"submenu-".$depth."\" style='".$style."'>";
    }

    function start_el(&$output, $item, $depth, $args) {
        $class_names = $value = '';

        $classes = empty( $item->classes ) ? array() : (array) $item->classes;  

        if ($depth == 0 &&
                (in_array("current-menu-item", $classes) ||
                 in_array('current-menu-parent', $classes) ||
                 in_array('current-menu-ancestor', $classes)) ) {
            $this->hidden = true;
        } else {
            $this->hidden = false;
        }
        parent::start_el($output, $item, $depth, $args);
    }
}

I'm sure this will need some optimization, but it works.

4
  • It should probably be if ($depth >= 0) in both cases, since presumably the user would want the same behavior at any level of depth; this wouldn't work for user-selected depths. Also, you could remove the $class_names = $value = ''; line entirely; it does nothing here and the analogous behavior will fire as expected when calling parent::start_el(). Apr 19, 2013 at 15:59
  • Given the default value, you can also drop the else { $this->hidden = false; } statement. Apr 19, 2013 at 16:07
  • Oh, and your semantics are perplexing! :) I believe, given how you have this implemented, that $this->hidden should actually be $this->visible. Apr 19, 2013 at 16:47
  • This returns the full menu for me, not just the sister pages. Any tricks for how to implement within the theme after adding this class to functions.php? Jul 29, 2015 at 18:36

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