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've been messing around / searching for hours and still can't get this to work, so i'm finally giving in and asking for some help.

I'm trying to write a custom walker that shows only the current pages children, or if there are no children display the pages siblings.

For example, take the following menu tree:

  • 1.0
    • 1.2.0
      • 1.3.0
      • 1.3.1
      • 1.3.2
    • 1.2.1
    • 1.2.2
  • 2.0

Let's assume that i am on the current page 1.2.0. On this page I want to display it's children (1.3.0, 1.3.1, 1.3.2)

however, if i'm on the page 1.2.2, since it doesn't have any children, it should display it's current level siblings, so it should show me (1.2.0, 1.2.1, 1.2.2).

share|improve this question
4  
Please move your solution to an answer so that it's more clear for others and questions doesn't haunt site as unanswered. –  Rarst Mar 1 '13 at 16:30
    
What @Rarst said! I almost missed that you'd come up with a solution. –  Chris Krycho Apr 19 '13 at 1:36
    
Necro answer. I asked more or less the same question on SO about 2 years ago with a very good answer. stackoverflow.com/questions/5826609/… –  Stoosh Jul 15 '13 at 0:48
    
Moved answer inside question to separate answer. OP: Please follow up there. –  kaiser Aug 6 '13 at 13:55

3 Answers 3

I had a similar experience. You may want to think about moving the pages logic out of the walker. Basically, compile the current page hierarchy as an object. Then use the 'exclude' parameter in the wp_nav_menu function. Now the excluded pages would depend on if the current page has children. If no children show brothers; if children && those children are the end of the line show brothers and children; if children && and grandchildren exist exclude brothers and show children and grandchildren.

share|improve this answer
    
What is this exclude parameter to which you refer? I'm looking at the documentation and don't see any reference to it. –  Chris Krycho Apr 19 '13 at 1:35
1  
I apologize I was mistaken. You are correct that there is no 'exclude' parameter. I meant to use "wp_list_pages" function. –  Steve Fischer Apr 22 '13 at 15:51
    
Very good, and no worries. I was just curious if there was something undocumented but in the back end—I have seen that happen before. Thanks for clearing it up! I hadn't thought of using wp_list_pages() in this context, so that's an interesting idea. –  Chris Krycho Apr 22 '13 at 18:09

As the OP never moved it to an answer and someone commented that he closely missed that there was a solution, I moved it to this answer. To the OP: Please repost this answer as your own, mark it as solution and leave a comment on this answer so I can remove it.

Finally hours later, i have something that is working. I'm thinking this isn't the best way to do it so some optimization would still be welcomed but for someone else looking for something similar, here is the code that is working.

<?php

class SH_Child_Only_Walker extends Walker_Nav_Menu {

    private $ID;
    private $depth;
    private $classes = array();
    private $child_count = 0;
    private $have_current = false;


    // Don't start the top level
    function start_lvl(&$output, $depth=0, $args=array()) {

        if( 0 == $depth || $this->depth != $depth )
            return;

        parent::start_lvl($output, $depth,$args);
    }

    // Don't end the top level
    function end_lvl(&$output, $depth=0, $args=array()) {
        if( 0 == $depth || $this->depth != $depth )
            return;

        parent::end_lvl($output, $depth,$args);
    }

    // Don't print top-level elements
    function start_el(&$output, $item, $depth=0, $args=array()) {

        $is_current = in_array('current-menu-item', $this->classes);

        if( 0 == $depth || ! $is_current )
            return;

        parent::start_el($output, $item, $depth, $args);
    }

    function end_el(&$output, $item, $depth=0, $args=array()) {
        if( 0 == $depth )
            return;

        parent::end_el($output, $item, $depth, $args);
    }

    // Only follow down one branch
    function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {

        // Check if element is in the current tree to display
        $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );
        $this->classes = array_intersect( $current_element_markers, $element->classes );

        // If element has a 'current' class, it is an ancestor of the current element
        $ancestor_of_current = !empty($this->classes);

        // check if the element is the actual page element we are on.
        $is_current = in_array('current-menu-item', $this->classes);

        // if it is the current element
        if($is_current) {

            // set the count / ID / and depth to use in the other functions.
            $this->child_count = ( isset($children_elements[$element->ID]) ) ? count($children_elements[$element->ID]) : 0;
            $this->ID = $element->ID;
            $this->depth = $depth;
            $this->have_current = true;

            if($this->child_count > 0) {

                // if there are children loop through them and display the kids.
                foreach( $children_elements[$element->ID] as $child ) {
                    parent::display_element( $child, $children_elements, $max_depth, $depth, $args, $output );
                }

            } else {
                // no children so loop through kids of parent item.
                foreach( $children_elements[$element->menu_item_parent] as $child ) {
                    parent::display_element( $child, $children_elements, $max_depth, $depth, $args, $output );
                }

            }
        }

        // if depth is zero and not in current tree go to the next element
        if ( 0 == $depth && !$ancestor_of_current)
            return;

        // if we aren't on the current element proceed as normal
        if(! $this->have_current )
            parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
    }
}

attach it like you would with any other custom walker...

<?php
    $menu = wp_nav_menu( array(
        'menu' => 'primary-menu'
        ,'container' => 'nav'
        ,'container_class' => 'subpages'
        ,'depth' => 0
        ,'walker' => new SH_Child_Only_Walker()
     ));
?>
share|improve this answer
    
sorry for such a long delay. I posted the answer by itself and marked it. –  jchamb Nov 11 '13 at 21:21
    
@jchamb No problem. Upvoted yours. Will delete mine on the fly as soon as you received this note. –  kaiser Nov 11 '13 at 23:28
    
@jchamb Please add all the notes to your answer as well, as a little explanation always never hurts. –  kaiser Nov 11 '13 at 23:29
up vote 2 down vote accepted

This is the walker I used to display only children of the current menu item. Or the menu items siblings if it doesn't have any children of its own.

There are comments throughout the class explaining each section

<?php

class SH_Child_Only_Walker extends Walker_Nav_Menu {

private $ID;
private $depth;
private $classes = array();
private $child_count = 0;
private $have_current = false;


// Don't start the top level
function start_lvl(&$output, $depth=0, $args=array()) {

    if( 0 == $depth || $this->depth != $depth )
        return;

    parent::start_lvl($output, $depth,$args);
}

// Don't end the top level
function end_lvl(&$output, $depth=0, $args=array()) {
    if( 0 == $depth || $this->depth != $depth )
        return;

    parent::end_lvl($output, $depth,$args);
}

// Don't print top-level elements
function start_el(&$output, $item, $depth=0, $args=array()) {

    $is_current = in_array('current-menu-item', $this->classes);

    if( 0 == $depth || ! $is_current )
        return;

    parent::start_el($output, $item, $depth, $args);
}

function end_el(&$output, $item, $depth=0, $args=array()) {
    if( 0 == $depth )
        return;

    parent::end_el($output, $item, $depth, $args);
}

// Only follow down one branch
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {

    // Check if element is in the current tree to display
    $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );
    $this->classes = array_intersect( $current_element_markers, $element->classes );

    // If element has a 'current' class, it is an ancestor of the current element
    $ancestor_of_current = !empty($this->classes);

    // check if the element is the actual page element we are on.
    $is_current = in_array('current-menu-item', $this->classes);

    // if it is the current element
    if($is_current) {

        // set the count / ID / and depth to use in the other functions.
        $this->child_count = ( isset($children_elements[$element->ID]) ) ? count($children_elements[$element->ID]) : 0;
        $this->ID = $element->ID;
        $this->depth = $depth;
        $this->have_current = true;

        if($this->child_count > 0) {

            // if there are children loop through them and display the kids.
            foreach( $children_elements[$element->ID] as $child ) {
                parent::display_element( $child, $children_elements, $max_depth, $depth, $args, $output );
            }

        } else {
            // no children so loop through kids of parent item.
            foreach( $children_elements[$element->menu_item_parent] as $child ) {
                parent::display_element( $child, $children_elements, $max_depth, $depth, $args, $output );
            }

        }
    }

    // if depth is zero and not in current tree go to the next element
    if ( 0 == $depth && !$ancestor_of_current)
        return;

    // if we aren't on the current element proceed as normal
    if(! $this->have_current )
        parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
}

Attach it like you would with any other custom walker on a wp_nav_menu

<?php
wp_nav_menu( array(
    'menu' => 'primary-menu'
    ,'container' => 'nav'
    ,'container_class' => 'subpages'
    ,'depth' => 0
    ,'walker' => new SH_Child_Only_Walker()
 ));
?>
share|improve this answer
    
I want to point out @Stoosh's comment pointing here. stackoverflow.com/questions/5826609/… as this is another good solution –  jchamb Nov 12 '13 at 3:38

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.