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'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...

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.

share|improve this question
add comment

3 Answers

up vote 2 down vote accepted

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.

share|improve this answer
    
Brilliant, that's certainly got me on the right track! Using a combination from another bit of code I've got it doing 90% of what I need it to do... –  a_kc Oct 24 '11 at 14:47
    
So, what 10% are you missing? :) –  Chip Bennett Oct 24 '11 at 14:50
    
I've got is so when there are no child pages it displays the top level pages. That's great, until it reaches the bottom of the tree - then it currently outputs nothing. Ideally then it would either list it's peers, or failing that it's parents. –  a_kc Oct 24 '11 at 15:04
    
See updated answer. :) –  Chip Bennett Oct 24 '11 at 15:27
    
I can't post code in here easily can I? Basically, I can see how that all that works! Alas I'm very frontend, and my lack of PHP knowledge is stopping me sticking it all together! –  a_kc Oct 24 '11 at 15:47
show 4 more comments

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
) );
share|improve this answer
add comment

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/

share|improve this answer
add comment

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.