0

I have a custom walker that extends the Walker class. My code uses the Walker_Page class as it's base as it does pretty much everything else I want but I need to modify it a bit so that it behaves a little differently.

Essentially, I want to prevent the walker from creating new levels of navigation for menu items that are not related to the current page -- that is I only want it to walk deeper levels when the current page is either the current page, or an ancestor of it.

The key to this seems to be the $current_page (current_object_id) argument that is passed to the start_el method. If this were also passed to the start_lvl method, I think this would be easy to code, but start_el seems to be the only method that gets it.

Any help/direction would be much appreciated.

I would paste my code here, but it's basically the same as Walker_Page

2 Answers 2

1

You may find this article I wrote over at WPtuts helpful.

The following example I've adapted from that article. It lists all the top leve links, but only explores ancestors of the 'current' menu item. Hopefully the logic is clear with the comments:

class WPSE73358_Ancestors_Only_Walker extends Walker_Nav_Menu {

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

        // Check if element as a 'current element' class
        $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );
        $current_class = 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($current_class);

        // If this is not the top level nor the current, or ancestor of the current menu item - stop here.
       if ( 0 != $depth &&  !$ancestor_of_current)
           return;

        parent::display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output );
    }
}

Although it extends the Walker_Nav_Menu it is written so that it can extend pretty much any of the Walker classes (including the base class). You may find that the classes need to be changed, or - for instance with posts, it may be more appropriate to use get_post_ancestors() instead.

3
  • Thanks for your help. I actually read that article recently! It will require some tweaking like you said as the elements of a Walker_Page don't have a classes property. I thought of using get_post_ancestors but I was concerned that may be adding extra db queries on every iteration. I suppose that info is all probably cached though? Commented Nov 21, 2012 at 14:34
  • Yes it is, using it is fine. You will need to determine which page ID to apply get_post_ancestors() to though - I assume the current page? Commented Nov 21, 2012 at 15:23
  • ..I almost got it. Will post my solution below when I get it figured out. Need to work out the logic so that it still shows posts that are children of the current page, not just ancestors. Commented Nov 21, 2012 at 21:45
0

I accidentally left out an important detail in my question that made it a little more complex to solve. I also wanted to show sub navigation of the current page if it had children.

I did this by comparing the element's ancestors to the current page's ancestors (as well as the current page itself), if there wasn't any match, the iteration was skipped.

Here is my solution (Thanks Stephen!)

class WPSE73358_Walker extends Walker_Page {

    function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {

        if ( 0 < $depth ) {

            $qoID = get_queried_object_id();

            // seperated for readability
            $current_ancestors = get_post_ancestors( $qoID );
            $element_ancestors = get_post_ancestors( $element->ID );

            $check = $current_ancestors;
            unset( $check[ count($check)-1 ] ); // remove the last ancestor - all pages will share this in common
            $check[] = $qoID; // include current page id

            $against = $element_ancestors;
            unset( $against[ count($against)-1 ] );
            // element id is unnecessary because we're checking against it's ancestry

            // check to see if the current page & current element have anything in common
            $results = array_intersect( $check, $against );

            // if current page & element have something in common, array will not be empty, and we can continue.
            // if not, move on to the next iteration.
            if ( empty( $results ) )
                return;
        }

        parent::display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output );
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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