It’s been a while since I’ve stepped foot inside the forrst and today I decided that it might be a good idea to drop in and see what wonderful things I could find. I came upon a link to an article titled Creating a hierarchical submenu in WordPress in which Roger Johansson presents a solution to creating a list of pages where only the active hierarchy is shown. I have always wondered why WordPress didn’t come pre-installed with such functionality myself. Reading Roger’s post inspired me to clean up a bit of code that I wrote previous this year which accomplishes this by extending the Walker_Page class.
Add to functions.php
/**
* Create HTML list of pages.
*
* @package Razorback
* @subpackage Walker
* @author Michael Fields <michael@mfields.org>
* @copyright Copyright (c) 2010, Michael Fields
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
* @uses Walker_Page
*
* @since 2010-05-28
* @alter 2010-10-09
*/
class Razorback_Walker_Page_Selective_Children extends Walker_Page {
/**
* Walk the Page Tree.
*
* @global stdClass WordPress post object.
* @uses Walker_Page::$db_fields
* @uses Walker_Page::display_element()
*
* @since 2010-05-28
* @alter 2010-10-09
*/
function walk( $elements, $max_depth ) {
global $post;
$args = array_slice( func_get_args(), 2 );
$output = '';
/* invalid parameter */
if ( $max_depth < -1 ) {
return $output;
}
/* Nothing to walk */
if ( empty( $elements ) ) {
return $output;
}
/* Set up variables. */
$top_level_elements = array();
$children_elements = array();
$parent_field = $this->db_fields['parent'];
$child_of = ( isset( $args[0]['child_of'] ) ) ? (int) $args[0]['child_of'] : 0;
/* Loop elements */
foreach ( (array) $elements as $e ) {
$parent_id = $e->$parent_field;
if ( isset( $parent_id ) ) {
/* Top level pages. */
if( $child_of === $parent_id ) {
$top_level_elements[] = $e;
}
/* Only display children of the current hierarchy. */
else if (
( isset( $post->ID ) && $parent_id == $post->ID ) ||
( isset( $post->post_parent ) && $parent_id == $post->post_parent ) ||
( isset( $post->ancestors ) && in_array( $parent_id, (array) $post->ancestors ) )
) {
$children_elements[ $e->$parent_field ][] = $e;
}
}
}
/* Define output. */
foreach ( $top_level_elements as $e ) {
$this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
}
return $output;
}
}
Add to any template file with a php extension:
$walker = new Razorback_Walker_Page_Selective_Children(); wp_list_pages( array( 'title_li' => '', 'walker' => $walker, ) );
Will recognize the child_of parameter.
$walker = new Razorback_Walker_Page_Selective_Children(); wp_list_pages( array( 'title_li' => '', 'child_of' => 1283, 'walker' => $walker, ) );



Really great. Thanks so much!
I know it may be a lot to ask, but would you consider adapting this walker class to work with adaptive post hierarchy?
I’m working on a photo gallery project and want to add a widget that does exactly what this walker does for pages. I’ve tried adapting it myself, but keep running into roadblocks.
Either way, thanks for such an amazing walker addition.
Great script. I have been searching for something like this for a while. Question: What if I would like to exclude a page form being listed? For example, I have many pages that link form other pages. Currently they are all listed. In other words, they are orphan pages. I am still getting my feet wet with WP custom functions etc., so i am thinking maybe there is a way to add a custom field that i can have a value of “no-list” and exclude those. Any help would be appreciated.
Thanks!
Thanks. Is the exclude parameter working?
Honestly, I did not test this functionality, but then again… I didn’t need it. I did however test the ‘child_of’ argument which should allow you to isolate a single branch of the page tree.
Thanks for the speedy reply! That worked. What I did was create a page called “EXCLUDE PAGES” and added the ID to ‘exclude’. This and any child pages will not show up. That way the client can add or remove pages without the need to hard code excluded pages. Again, thanks a million!
Nice work. I have a small question though, is it possible to exclude the all top level pages?
So instead of:
- Section 1
– sub page (current)
– sub sub page
– sub page 2
- Section 2
- Section 3
It outputs:
- sub page (current)
– sub sub page
- sub page 2
At present it outputs the whole tree from the top, I have been trying to get it to only output and walk from a “section” (e.g.: 1 level deep). I know i could hide/remove the top level pages with CSS but it would be great if I could exclude top level pages from the query.
Thanks Matt! It should be relatively easy to do this. Have you tried something like this?
if ( is_page() ) { $get_children_of = ( isset( $post->ID ) ) ? (int) $post->ID : 0; $walker = new Razorback_Walker_Page_Selective_Children(); wp_list_pages( array( 'title_li' => '', 'child_of' => $get_children_of, 'walker' => $walker, ) ); }Utter genius. Exclude pages works perfectly. Finally a sensible & easily implemented page menu structure for WP. Thanks!
Thanks Michael, I gave that snippet a go and it kind of works but parent and sibling pages seem to disappear as I move down the tree.
Ideally I was trying to keep these pages in the tree and only remove the very top level? e.g.:
- second level page
—- third level page (current)
- second level page 2
Right on… In that case you will want to use the same idea, but instead of passing $post->ID to the child_of argument, you will want to get the ID of the root page and pass that instead. A good place to start is the code posted here.
Thank you many times over! I’ve been searching the web and trying out solutions with no luck for days. Your solution was elegant and worked. Just what I needed. Why aren’t more people making use of the Walker-Page class? This is the first I’ve heard of it.
Thanks again.
Hi… Is it possible to see this functionality implemented somewhere? I don’t know really know how to code, (although I can experiment) but I’d like to have a better idea of what the above code achieves.
Best,
Joel.
Nice walker ;). I have done something similar for custom menus http://wordpress.org/extend/plugins/advanced-menu-widget/ – maybe somebody will find it useful.