wp_list_pages() but only show children on the branch you are on

by Brady   Last Updated July 11, 2019 20:08 PM

Lets say I have this menu:

Top
    |___    Sub 1
            |___    Sub Sub 1
            |___    Sub Sub 2
            |___    Sub Sub 3
            |___    Sub Sub 4
    |___    Sub 2
            |___    Sub Sub 1
            |___    Sub Sub 2
            |___    Sub Sub 3
            |___    Sub Sub 4
    |___    Sub 3
            |___    Sub Sub 1
            |___    Sub Sub 2
            |___    Sub Sub 3
            |___    Sub Sub 4
    |___    Sub 4
            |___    Sub Sub 1
            |___    Sub Sub 2
            |___    Sub Sub 3
            |___    Sub Sub 4

I can list that menu using this:

    if( 0 == $post->post_parent && 0 == count( $children ) )
        return;

    if( 0 == $post->post_parent )
    {
        $child_of = $post->ID;
    }
    else {
        $parents = get_post_ancestors( $post->ID );
        $child_of = end( $parents );
    }

    $args = array
    (
        'child_of' => $child_of,
        'echo' => 0,
        'title_li' => ''
    );

    $pages = wp_list_pages( $args );

This is fine but I don't want to show all page items. What I want is that only imediate children of the page you are on is shown.

So if I'm on page Top the menu should appear like so:

Top
    |___    Sub 1
    |___    Sub 2
    |___    Sub 3
    |___    Sub 4

If I'm on page Top/Sub 3 the menu should appear like so:

Top
    |___    Sub 1
    |___    Sub 2
    |___    Sub 3
            |___    Sub Sub 1
            |___    Sub Sub 2
            |___    Sub Sub 3
            |___    Sub Sub 4
    |___    Sub 4

And so on, so that it can work to any depth.

Rarst put up a nice answer but this was for when using WordPress menus. I'm looking for the same for wp_list_pages(). Looking for an answer that uses a Walker or filters/hooks. I know how to solve this problem with CSS but this doesn't fix the problem of unnecessary HTML being sent to the browser.



Answers 3


You can consider using a mixture of get_children() for the children of the page and page siblings and get_post_ancestors() for the ancestors:

global $post;

$pages =& get_children(array(
  'post_type' => 'page',
  'post_parent' => $post->ID,
));

$siblings = get_children(array(
  'post_type' => 'page',
  'post_parent' => $post->post_parent,
));

$ancestors = get_post_ancestors($post->ID);

Edit - a quick demonstration of the idea which can use some clean up:

<ul>
<?php
    global $post;

    $children =& get_children(array(
        'post_type' => 'page',
        'post_parent' => $post->ID,
    ));

    $siblings = get_children(array(
        'post_type' => 'page',
        'post_parent' => $post->post_parent,
    ));

    $ancestors = get_post_ancestors($post->ID);

    if(!empty($ancestors)):
        $ancestors = array_reverse($ancestors);
        foreach($ancestors as $aid):
            $p = get_post($aid);
            ?>
            <li>
                <?php echo $p->post_title ?>
                <ul>
        <?php endforeach?>

        <?php if(!empty($siblings)): ?>
            <?php foreach($siblings as $sibling): ?>
                <li>
                    <?php echo $sibling->post_title?>
                    <?php if($sibling->ID == $post->ID && !empty($children)): ?>
                        <ul>
                            <?php foreach($children as $child): ?>
                                <li><?php echo $child->ID ?></li>
                            <?php endforeach?>
                        </ul>
                    <?php endif ?>
                </li>
            <?php endforeach ?>
        <?php endif ?>

        <?php foreach ($ancestors as $aid):?>
            </ul></li>
        <?php endforeach ?>
    <?php endif  ?>
</ul>
Nikolay Yordanov
Nikolay Yordanov
March 14, 2012 16:51 PM

I have come up with my own solution but I'm not convinced it's the best.

    $parents = array( $post->ID );
    if( 0 != $post->post_parent )
    {
        $parents = array_merge( $parents, get_post_ancestors( $post->ID ) );
    }
    $child_of = end( $parents );

    $args = array
    (
        'child_of' => $child_of,
        'echo' => 0,
        'title_li' => '',
        'walker' => new chg_Sub_Page_Navigation_Walker( $parents )
    );

    $pages = wp_list_pages( $args );

Walker:

class chg_Sub_Page_Navigation_Walker extends Walker_Page
{
    var $parents = array();

    function __construct( $parents )
    {
        $this->parents = $parents;
    }

    function start_el( &$output, $page, $depth, $args, $current_page )
    {
        if( in_array( $page->post_parent, $this->parents ) )
            parent::start_el( &$output, $page, $depth, $args, $current_page );
    }

    function end_el( &$output, $page, $depth )
    {
        if( in_array( $page->post_parent, $this->parents ) )
            parent::end_el( &$output, $page, $depth );
    }
}
Brady
Brady
March 14, 2012 17:48 PM

Considere use the argument "depth"

Example:

wp_list_pages( 'sort_column=menu_order&title_li=&child_of=' . $post->ID . '&echo=0&depth=1' 
Eliut Islas
Eliut Islas
July 11, 2019 19:12 PM

Related Questions


Updated February 15, 2019 10:08 AM

Updated February 13, 2019 17:08 PM

Updated June 04, 2015 07:03 AM

Updated March 28, 2015 09:02 AM

Updated July 25, 2015 13:03 PM