Show just one level of child pages, wp_list_pages woe

I’m working on a site that has a fairly large page structure a few levels deep – in some sections there are a lot of pages.

The basic setup is something like this…

Read More
Parent
Parent
Parent
-Child
-Child
--Grandchild
--Grandchild
--Grandchild
---GreatGrandchild
-Child
-Child
--Grandchild
--Grandchild
--Grandchild
---GreatGrandchild
-Child
Parent
-Child
Parent
Parent
-Child
-Child
--Grandchild
---GreatGrandchild
--Grandchild

You get the picture – just imagine a lot more pages!

My normal solution of submenus works fine normally (taken straight from the Codex), because I’m working on much smaller sites. This site results in a massively long menu that is far to big to be useful.

What I would like to do is just to show the level directly below the page that is currently being viewed. Basically an ‘In this section…’ menu.

No matter what combination of wp_list_pages snippet I’ve found I’ve only been able to get all or nothing.

Also because there are some sections that don’t have children it needs to just show the top level links when no children are present.

Hopefully that makes sense – I’ve been tearing my hair out all day over this! Any help greatly appreciated!

EDIT

Right, I’ll ad it here. Sorry for being a noob!

So huge thanks to @chip-bennet for getting me this far!

I now have this code that does nearly everything I need it to.

$output = wp_list_pages('echo=0&depth=1&title_li=Sections' );

        if (is_page( )) {

          $page = $post->ID;

          if ($post->post_parent) {
            $page = $post->post_parent;
          }

          $children=wp_list_pages( 'echo=0&child_of=' . $page . '&title_li=' );


          if ($children) {
            $output = wp_list_pages( array(
                // Only pages that are children of the current page
                'child_of' => $post->ID,
                // Only show one level of hierarchy
                'depth' => 1,
                'title_li' => 'In this Section'
            ) );
          }


        } 

        echo $output;

This works exactly as I want until I hit the bottom of the tree, when it outputs nothing.

Chip has given me the code again to show the peers of the page rather than looking for more children – however my lack of proper PHP skills is meaning I’m having trouble adding it to this code.

Related posts

Leave a Reply

4 comments

  1. This should work, using nothing more than the available argument-array parameters for wp_list_pages(): specifically, depth and child_of.

    To display one level of hierarchy, for descendant pages of the current page:

    <?php
    // Globalize the $post variable;
    // probably already available in this context, but just in case...
    global $post;
    wp_list_pages( array(
        // Only pages that are children of the current page
        'child_of' => $post->ID,
        // Only show one level of hierarchy
        'depth' => 1
    ) );
    ?>
    

    There should be no need to resort to a custom walker class.

    EDIT

    TO show the top-level links as well, simply change a couple of the parameters:

    <?php
    // Globalize the $post variable;
    // probably already available in this context, but just in case...
    global $post;
    wp_list_pages( array(
        // Only pages that are children of the current page's parent
        'child_of' => $post->post_parent,
        // Only show two level of hierarchy
        'depth' => 2
    ) );
    ?>
    

    Note the change of 'child_of' => $post->ID to 'child_of' => $post->post_parent, which will include pages of the current page’s parent page, and the change of 'depth' => 1 to 'depth' => 2, which will include the current page’s siblings, and their children.

    EDIT 2

    Okay, here’s how I would handle the code you just added. First, I would use a proper array, rather than an array string. Then, I would query for context/child-pages before building the wp_list_pages() argument array, then, just call wp_list_pages() once:

    // Use parent page if exists, else use current page    
    $child_of_value = ( $post->post_parent ? $post->post_parent : $post->ID );
    // Depth of 2 if parent page, else depth of 1
    $depth_value = ( $post->post_parent ? 2 : 1 );
    // Build argument array
    $wp_list_pages_args = array( 
        'child_of' => $child_of_value,
        'depth' => $depth_value,
        'title_li' => 'Sections'
    );
    // Now, output the result
    wp_list_pages( $wp_list_pages_args );
    

    We’ll need more complex code, if you actually need to output separate lists.

  2. Well, you can use the depth argument to control how many levels deep you want to show, and I’ve created a SubMenu walker, which displays the menu starting from the direct children of the current page. Code:

    class My_Walker_Submenu extends Walker_Nav_Menu {
    
    
      function display_element($element, &$children_elements, $max_depth, $depth = 0, $args, &$output) {
    
        if (! $element)
          return;
    
        $itemId = null;
    
        if ( $depth == 0) {
            if (!isset($args[0]->child_of)){
             return;
            }
          foreach ( $children_elements as $id => $children ) {
            $data = get_post_meta ( $id, '_menu_item_object_id', true );
            if ($data == $args [0]->child_of) {
              $itemId = $id;
              break;
            }
          }
          if ($itemId == null) {
            return;
          }
          unset($args [0]->child_of);
    
          foreach ( $children_elements [$itemId] as $child ) {
            parent::display_element ( $child, $children_elements, $max_depth, $depth + 1, $args, $output );
          }
          return;
    
        }
        return parent::display_element ( $element, $children_elements, $max_depth, $depth, $args, $output );
      }
    

    And usage (I’m using it in the sidebar actually):

    echo wp_nav_menu( array(
                  'container' => '',
                  'theme_location' => 'header-menu',
                  'walker' => new My_Walker_Submenu(),
                  'child_of' => $page->ID, 
                  'depth'   => 2
    ) );
    
  3. This is a problem that I see come up quite often. I think that the WP_List_Pages solutions I see all over are a bit old school now that WordPress has a proper menu manager (Appearance -> Menus).

    I have tried numerous solutions including some plugins/widgets and walkers like the one above. I have never been very pleased with the available solutions. I created my own plugin that handles these secondary menus the way that I prefer and am used to. When displaying the menu you can set a start_depth to inform the menu to ignore a certain number of ‘levels’ above your current page.

    So for your needs you would set the start_depth to 1, and it would automatically ignore the top level navigation items when displaying your menu.

    I call the plugin WP Nav Plus because it builds upon the currently power and options of wp_nav_menu, just with the additional of the start_depth argument for displaying child menus.

    For anyone who is interested it is available at my website: https://mattkeys.me/products/wp-nav-plus/