WordPress nav, solved: How to list child pages only for the current Parent page

How to list WordPress child pages ONLY for the current parent page

The Problem:

  • You have a 3-tiered navigational hierarchy, like above.
  • If you click on mainPage-a, you only want the 3 child Pages to show.
  • But if you click to dive into Page1, you still want to show the three child Pages of mainPage-a, but also want Page1′s subpages to show, but ONLY those subpages.

For a live example, this site presented the problem.  Click one of the four links, and you’ll see child pages listed ONLY for that parent.  We are moving the entire site into WordPress, and wanted it to run on the fewest page templates possible.  I want the client to simply focus on adding pages and designating a parent, without having to worry about a complex set of templates so that the nav would work.

If you’re like me…

You’ve searched Google for solutions, tried a bunch of PHP from the WordPress Codex, tried some blogs’ suggestions to no avail, and pulled chunks hair from both sides of your aching head.  (Or maybe you said screw it and just created a bunch of page templates.)  But here is a solution that works with one (1) WordPress page template to power the above site’s Programs section.

The Solution:

Simply said: don’t just rely on PHP.  This is key. (It was my folly in the beginning, and likely yours if you’re reading this.)

Every solution I tried from the Codex spits out child pages and parent pages, but sometimes either…

  • ALL child pages are returned…
  • some child pages are returned with NO parent pages except the current page…
  • or some other result that loses the idea above.
  • So much spitting.

The simplest fix I found is to work with what you can use to 1.) program a WordPress template (uh huh, PHP), plus 2.) some styling of the HTML that WordPress spits out.

Just a few lines of PHP…

Take this, which is adapted from the Codex:

<?php if(!$post->post_parent){
$children = wp_list_pages("title_li=&child_of=".$post->ID."&echo=0");
}else{
if($post->ancestors)
{
$ancestors = end($post->ancestors);
$children = wp_list_pages("title_li=&child_of=".$ancestors."&echo=0");
}
}
if ($children) {
?>
<ul> <?php echo $children; ?></ul>
<?php } ?>

It’ll spit out all pages, and will do so if you’re on a Page or subpage.  But you don’t want Page2 nor Page3′s stuff showing up when you’re wanting Page1′s stuff.

WordPress rocks

Why?  Because it marks up all the <li>‘s it returns on a page, to be styled if/when later.  With the above, it’ll encapsulate all the subpages of a given Parent into <ul>‘s with a class of “children“.  Also, since WordPress is like the ever-watchful eye of where your users are, it adds classes to the <li>‘s to denote the current-page, that page’s parent, ancestor, etc.

Here’s the elegance (I think, anyway):  hide all the <ul>‘s with a class of “children”.  Then, show <ul class=”children”> only if it is contained in a <li> with any of these three classes: “current_page_item“, current_page_parent“, or “current_page_ancestor“.  The CSS looks like this, below:

.children
{ display: none; }

.current_page_item .children,
.current_page_ancestor .children,
.current_page_parent .children

{ display: block; }

Super slim coding, no?! Here’s a bit more explanation…

 .children
{ display: none; }

  • With the above PHP we know that WordPress will spit out the entire  family tree for the current section of the site we’re on. Then we’ll hide it immediately with CSS. .

.current_page_item .children,
.current_page_ancestor .children,
.current_page_parent .children
{ display: block; }

  • Now that ALL pages are in the nav, just hidden, let’s show only those pages encapsulated in the list-item that has one of these three classes. Now, we get only the child pages we want for the current section. Nice.

In the end

No standalone PHP seems to accomplish the task, nor is a plugin needed.  jQuery is perhaps an option, but since that will only render when the document’s ready, vs. CSS which hides it all before the page shows to the user, there is no momentary blip of subnav.  It’s all hidden, and only what’s needed shows.

If you found it useful, let me know!

Happy to hear if this helped others.

68 Comments

  1. Comment by Brenda
    on March 7, 2011

    Excellent, exactly what I needed
    Thanks for the CSS trick

  2. Comment by Marc Falk
    on March 31, 2011

    Brilliant solution. As you, I searched for ages, and all kinds of solutions exist out there, but this is the most simple one I’ve seen so far. Thank you.

  3. Comment by Mike Lee
    on April 14, 2011

    You’re very welcome, Marc. Glad it could help.

  4. Comment by samuel
    on April 25, 2011

    there seems to exist a standalone php (well a plugin actually) which accomplishes this task for (almost) years…

    http://www.webspaceworks.com/resources/wordpress/76/

    http://wordpress.org/support/topic/fold-page-list-plugin

    easy to implement (install plugin and add one line of php code) and still working (guess it still does with wordpress 3+).

  5. Comment by Mike Lee
    on April 25, 2011

    Thanks, Samuel. Mechanistically, it seems to be a fairly similar solution in to what I posted.

    The goal with my above solution, however, wasn’t simply to get all pages from the root to the current, grandchild page, but to do so while hiding the grandchildren of second-level pages. This is what the CSS above accomplishes. I could not find any standalone PHP anywhere to accomplish that, and I did not want a plugin, no matter how simple the plugin might be. Perhaps that is a solution for this entirely in PHP, but for now the PHP + CSS helps.

  6. Comment by Fannar
    on July 19, 2011

    Thanks for sharing ! Lifesaver :)

  7. Comment by Stoppy
    on August 17, 2011

    can be do quite easily with css, something like this:

    div#nav ul li ul
    {
    display:none;
    }

    div#nav ul li.current_page_item ul,
    div#nav ul li.current_page_ancestor ul
    {
    display:block;
    }

  8. Comment by Mike Lee
    on August 17, 2011

    Thanks, Stoppy. Your CSS is similar to the CSS I wrote in the post, though, and is unfortunately less optimized while also potentially not being specific enough.

    My declaration of “.children” targets the unordered-list that WordPress will specifically output, but you’re targeting any unordered-list that may be within “current_page_…” list-items.

    In addition, it’s best to minimize descendant selectors in CSS. Your CSS unfortunately uses many descendant selectors, and also overly qualifies some selectors with tag keys. It’s best to write it simpler for browser rendering & page loading speed, which these days helps with Google indexing rank.

  9. Comment by christa
    on December 19, 2011

    Thank you so much for posting this! I was able to use the CSS with an existing plugin – I can’t believe there aren’t plugins that offer this already – but very clever solution, indeed.

  10. Comment by Huzoor Bux
    on January 4, 2012

    Hey Man i was looking for this since hours but failed you safe my life Thanks…… :)

  11. Comment by Mike Lee
    on January 4, 2012

    You bet! More WordPress solutions to post soon, and glad this one helped!

  12. Comment by Josh
    on January 17, 2012

    Hi Mike, I feel like I’m so close to getting this. I’m putting it into a Thesis theme, adding with a hook via the custom_functions.php file. The div I’m wrapping your PHP in comes up, but when I view the source, all I see is:

    <!–?php echo $children; ? –>

    none of the child pages are showing. frustrating! what am I missing? any suggestions?

  13. Comment by Mike Lee
    on January 17, 2012

    Hi Josh – looks like there are some extra characters in the code snippet, as though it’s commented with HTML comments. You’ll want to get rid of the “!–” at the beginning and the “–” at the end.

    Try using this and see if it works?

    <?php echo $children; ?>
  14. Comment by Josh
    on January 18, 2012

    baa! haha. how did that exclamation point get in there :) Thanks Mike. So, that is solved. Now, all the nav is showing up tho. Like, all of the pages. I go to a sub-page and it’s showing all the pages and children as well… probably best to show an image…

    http://blog.keyshot.com/wp-content/uploads/2012/01/ks-nav.jpg

    looking at the elements, the css rules are being triggered. Is there more to do for this to be done?

  15. Comment by Mike Lee
    on January 18, 2012

    Nice, glad that fixed it.

    I can see the image, but it looks like none of the styles are rendering. Is there a link I can check out? Is the stylesheet loading correctly?

  16. Comment by Josh
    on January 18, 2012
  17. Comment by lena
    on June 8, 2012

    Fantastic. Surprisingly hard to find this solution.

  18. Comment by Tayler
    on September 21, 2012

    This is fantastically simple (to a hard to find) solution. Thank you for sharing.

  19. Comment by Leslie
    on October 4, 2012

    Thank you for the awesome code! You’ve saved this designer a lot of heartache. Only thing is, I’m having a little trouble styling the menus with CSS…

    I can’t seem to get at that 2nd level menu.
    http://dev.angelsrescue.org/adopt/adoption-forms/

    I’ll keep trying things and hopefully I can make it work. :)
    Thanks again!

  20. Comment by Mike Lee
    on October 4, 2012

    So honored for the comment from a dog-rescue person! Glad it helped you, and glad it’s helping such a worthy-cause site!

    As for your second level menu, are you simply trying to style it? You could try:
    .submenu .children { your rules here }
    which should do the trick?

  21. Comment by Leslie
    on October 4, 2012

    Thank you! We’re hoping to get this redesign up soon, so we can keep saving the critters!

    I’m having all kinds of crazy happen. I started out trying
    .submenu .children
    but then the 3rd level menu started appearing on all of the Adopt pages. I had it working, but still had the issue of the content not bumping down when the 3rd level was showing. I changed some things, and now i’ve got
    http://dev.angelsrescue.org/adopt/resources/
    the 3rd level showing (but not styled when not on the page it should be showing on) and making the 2nd level menu wrap below it.

    I think at this point, I should probably call it a day and come at it fresh in the morning. If you notice anything glaringly obvious, let me know. :)

    Thanks again for the code and the ridiculously fast response!

  22. Comment by Leslie
    on October 5, 2012

    Alright. I’ve got it almost straightened out. :)

    I’m having an issue with positioning in relationship to the page content. If you look at http://dev.angelsrescue.org/adopt/featured-pets/ and then http://dev.angelsrescue.org/adopt/adoption-forms/ you can see that the 3rd level menu isn’t bumping the page content down. It’s just floating on top of everything. Is there a way to make it play nice? I’m about to abandon the idea of the horizontal 3rd level and go to drop downs…

  23. Comment by Mike Lee
    on October 5, 2012

    Heya Leslie – Just took at look at those two pages. I can’t see any second or third level menus inside the .submenu unordered list. Am I missing where it should be?

  24. Comment by Benson
    on November 4, 2012

    Mike, I tried to use your css code to custom our website. But I am failed. Here is what I want to do as below
    Power solution, products, About Us, Support, Blog, Contacts (this is the main menu, top level menu)
    In the main “products”, it has children menu and grandchildren menu. like this:
    products (top level menu)–> inverter chargers (children menu) –> overview,PG series…(grandchildren menu).
    I want to hide the grandchildren menu but I failed following your code.
    I have the other question, My site has main menu and also has left navigation. The two menus have some relationship. Just like inverter charger (children menu in main navigation but the top menu in left nav.) I want to hide its sub navigation but unfold when click it. But if I click another menu, it will fold again. Can you help me? Thanks!

  25. Comment by Mike Lee
    on November 5, 2012

    Hi Benson – I can try to help. Can you send a link to see where you’re having the problems?

  26. Comment by Peter
    on November 27, 2012

    Hi there, Im not sure if you can help but I’m using the thesis theme and I would like to hide the child pages all together. I simply want a parent page to recognize and be highlighted with the selected background color without the child pages appearing in the menu.
    Hope this made sense… Any Ideas?

  27. Comment by Mike Lee
    on November 28, 2012

    Hi Peter – I could certainly try to help but just need some examples. Preferably a url and the WP function outputting the pages, if you have the latter. I don’t use the thesis theme but it wouldn’t be hard to debug. Any example pages/code you can link to?

  28. Comment by Nick
    on December 1, 2012

    This was driving me NUTS for the last few hours and I came across your post. Works awesomely. Thank you so much. There is SO much information about WordPress code that unfortunately it can take a lifetime to find the actual “right” answer lol. Thanks again.

  29. Comment by Mike Lee
    on December 3, 2012

    Glad it helped, Nick. Indeed, there are many ways to do any one thing in WP. I always prefer for the simplest (yet still secure) methods.

  30. Comment by Marie
    on December 26, 2012

    This was a huge help! Been trying to customize lots of different code and it never seemed to work. Thanks for the work-around!

  31. Comment by Devin Walker
    on December 29, 2012

    Hey – Thanks for the bit of code. It works EXACTLY how I want. Your website looks great also… keep up the good work. This grunt from the WP community thanks you :]

  32. Comment by Huw Rowlands
    on January 22, 2013

    Thank you ever so much for this snippet – been trying to do this for days!

  33. Comment by Dave Ellis
    on January 23, 2013

    I’ve been looking around for something like this all night – and your solution is almost perfect. There’s just one issue that I’m having…

    Where a current page contains children that also contain children it displays all the instances of children rather than those that relate specifically to the current page – wow it’s hard to explain!

    • Comment by Mike Lee
      on January 24, 2013

      Hi Dave – glad it {nearly} helped! Happy to see if I can help further too – is there an example page you can share?

      • Comment by Dave Ellis
        on January 26, 2013

        Hi Mike,

        Thanks for replying, I actually got this sorted with a little more css. Essentially what I needed was to hide .children .children

        Thanks again!

  34. Comment by marina
    on February 17, 2013

    HA! thank you very much =)
    exactly what i looked for and worked at first go!!!

  35. Comment by Tristan
    on March 28, 2013

    Hi,
    This is great, thanks!
    I tried plugins like flexipages, but they don’t work along well with polylang, and suddenly show other language pages also.
    One question, how would it be possible to hide current page grandchildren also, so that only the next level children are visible?
    They seem to both inherit the ‘children’ class and the page-id only so I can’t distinguish children from grandchildren =(

    • Comment by Mike Lee
      on March 28, 2013

      Hi Tristan – it’s likely possible to do this using by targeting immediate children of your element. Do you have an example to show that I can see?

      • Comment by Tristan
        on May 6, 2013

        Hi, unfortunately, the site is not live, but I found that the grandchildren are hidden like this:

        .current_page_item ul li li {display: none; }

        At least this works for grandchildren of 2nd level entries

      • Comment by Tristan
        on May 6, 2013

        Hi,
        sorry for the late reply, I had lost the bookmark..
        Basically, This worked out in the end on the 2nd level.
        .current_page_item ul li li {display: none; #widgets}

        However, I am stuck with the problem that for lower-level navigation, sister-pages children are also shown:

        level1
        level2
        lev3-current page
        my children
        level3 sister page
        unwanted children

        sister page is in children class of ancestor, so I can’t hide its pages =(

        • Comment by Mike Lee
          on May 14, 2013

          Hi Tristan – what are the classes assigned to the current page and sister page?

  36. Comment by Leif
    on March 31, 2013

    I know this is an old post. But i have a problem. I made the li’s display:inline; to make the navs horisontal. But my problem is that the children li’s push the parent li’s down on a new line. I would have to “separate” nav’s under each other. Is this possible?

    • Comment by Mike Lee
      on April 1, 2013

      Hi Leif – yes, it’s possible for sure. But it’s always good to see an example – do you have a link or pastebin.com example for me to see?

  37. Comment by Ian
    on April 24, 2013

    Hi I can not get the extention .php to show up on the child url’s when using this solution does anyone know how to solve this? WIthout it the parent link in my primary nav does not stay highlighted when I ckick on the child page.

  38. Comment by Jaydeep
    on June 19, 2013

    Thanks a lot!!! Excellent Solutions.

  39. Comment by David Pearson
    on July 3, 2013

    Fantastic stuff, wish i found this a few days ago! Props to you sir!

  40. Comment by Kurt
    on August 22, 2013

    Is there a way to accomplish this by reading the menu parent child relationship instead of the post/page parent child relationship. I would rather display my menu structure as I don’t want to show all pages and I have some pages that exist as menu children but in your code it won’t show those menu children because it is reading my post page hierarchy instead of my menu hierarchy. Does that make sense?

    • Comment by Mike Lee
      on August 22, 2013

      Hi Kurt – I’m trying to understand but not sure I follow. What does the markup look like? Or, if you have a link I could take a look to see what you’re describing.

  41. Comment by nitin
    on September 6, 2013

    i m new to wordpress but what i have seen in the demo is exactly what i want for my site could you please explain where i should paste this code to have this effect it is really urgent i would appreciate if you could help me at the earliest

  42. Comment by terryago
    on September 29, 2013

    Total lifesaver. Been searching for hours for this solution.

  43. Comment by Francis Boudreau
    on November 4, 2013

    Thank you very much! Exactly what I was looking for! And I guess the SEO juice flows better with this technique.

  44. Comment by Jeff Kinley
    on November 6, 2013

    Hey, this is great! So close to exactly what I need. I am, however, trying to expand on this concept, but not getting very far. You see, I am a newb when it comes to PHP. Anyway, I am trying to make it so that the nav is only for the children of say a parent page ‘services’. That way you can have a Services nav on every page. Does this make sense? Any ideas how to alter the PHP?

  45. Comment by Jeff Kinley
    on November 6, 2013

    Nevermind. I found this in the Codex:

    $ancestor_id));
    $incl = “”;

    foreach ($descendants as $page) {
    if (($page->post_parent == $ancestor_id) ||
    ($page->post_parent == $post->post_parent) ||
    ($page->post_parent == $post->ID))
    {
    $incl .= $page->ID . “,”;
    }
    }?>

    $ancestor_id, “include” => $incl, “link_before” => “”, “title_li” => “”, “sort_column” => “menu_order”));?>

    Combined with your CSS. I think its doing exactly what I want.

    • Comment by Mike Lee
      on November 6, 2013

      Hey Jeff – glad it worked. If I understand what you’re wanting correctly, my guess is you could use your code or mine on a page template for “services”. But it sounds like your code may work, which is great too.

  46. Comment by Nicklas
    on November 6, 2013

    i don’t get it. I searched the whole web for a solution for this, but every time i found something doesn’t it output anything at all. same problem with this. Im running wordpress 3.7.1 .. Any suggestions?

    • Comment by Mike Lee
      on November 6, 2013

      Hey Nicklas – do you have an example I can see? Or are you able to provide the Page structure you’re working with in WordPress as well as how you’re executing the code? If nothing is outputting I’m wondering what other issues WordPress may be having. If it’s a dev site, try to set wp-debug equal to “true” in wp-config.php and see if you get any errors?

  47. Comment by Jeff
    on November 7, 2013

    Mike,

    I was playing more with the code I found on the Codex. As it turns out, it does not need any CSS to hide anything. This code just shows only the children of the parent that you click on. All other children from other parents do not show. Perhaps you could use this to rewrite your code snippet so that it’s not dependent on the CSS. Here is the code I used, but you can get it from the Codex under wp_list_pages. This seems to be working well, though this is getting a bit into the deep end for me. Hope this helps.

    $ancestor_id));
    $incl = “”;

    foreach ($descendants as $page) {
    if (($page->post_parent == $ancestor_id) ||
    ($page->post_parent == $post->post_parent) ||
    ($page->post_parent == $post->ID))
    {
    $incl .= $page->ID . “,”;
    }
    }?>

    $ancestor_id, “include” => $incl, “link_before” => “”, “title_li” => “”, “sort_column” => “menu_order”));?>

    • Comment by Mike Lee
      on November 8, 2013

      Hey Jeff – I think you’re referring to this in the Codex: http://codex.wordpress.org/Function_Reference/wp_list_pages#List_all_top-level_pages_and_subpages_only_of_this_parent

      The full code doesn’t appear to be copied/pasted in your example above, though. What’s missing is the variable declaration for:

      $ancestor_id

      The link above shows the full code needed. This snippet is nearly the same as what I am showing, except that you have to declare a page id in the above variable. So this means it has to be page/template dependent, which requires more high-touch (read: hacking) instead of being a code snippet you can drop anywhere without having to grab/paste a page id.

      This latter idea of needing a specific page id is particularly important in that it can fail if you have Dev vs Live environments that don’t share exactly the same WP page ids. We’ve seen this a lot, so we try to have code snippets that work more generally regardless of a theme or WP environment.

  48. Comment by jeff
    on November 11, 2013

    Mike,

    Is there really no way to do this with only PHP? This seems so silly to me. While I agree your solution works, I just can’t believe there is not another way to do this with PHP. Any no plugin that can do this simple yet powerful task. So weird.

    • Comment by Mike Lee
      on November 11, 2013

      Hey Jeff – I agree with you. It seems there is possibly a PHP-only solution, but as a UX designer/developer, I don’t have the chops to dive deep into PHP deep enough to sort it out. I searched high and low and couldn’t find a PHP-only solution, so I combined two languages and wrote this post to share my result. :)

      Really, it’s not bad to separate some client-side vs. server-side tasks. By keeping the PHP to something relatively easy for the server to spit out, the remaining work is left for the client, i.e. browser, which is also an easy task for any browser, modern or not (it’s a simple show/hide operation). Server vs client decisions aren’t trivial (especially as an site or application scales), and in this case it’s a good compromise that helps everything stay optimized.

  49. Comment by md
    on December 17, 2013

    Hey, this is an awesome solution, but would you know how to include categories that are being used as menu items as well? Right now the child pages are showing on the page, which is great, but I also have categories that I would like included in the menu.. is there an easy solution that you know of for this? I’ve been searching forever to accomplish this and your post is the closest I’ve come.

  50. Comment by Animish
    on January 26, 2014

    Hey Mike!

    Thanks for this guide.

    I want to know the difference between

    $post->ancestors

    and

    $post->post_parent

    . Can you explain it?

    • Comment by Mike Lee
      on January 26, 2014

      Hi Animish – they are properties of the $post object, and they differ in how far up the “family tree” you’re moving in wp_list_pages. In the example that prompted this blog, we descended into the navigation beyond just the parent, and so WordPress displays the hierarchy of the $post object by adding classes to let us know if the list-item is a post_parent (i.e. the page I’m on is an immediate child of the top level page) or whether the list-item is a post_ancestor (meaning I’m on a grand-child page). Hope that helps but let me know if there are more questions.

  51. Comment by Erik
    on September 2, 2014

    Nice one Mike! I’ve been working on a solution for braincancer.org. They wanted the entire site in wordpress without changing the design too much. So child and grand children end up in the left nav and hide/display the subpage content. This is a good example http://braincancer.org/what-is-cancer. I like your solution way better than my hack :)

    • Comment by Mike Lee
      on September 2, 2014

      That’s a nice solution as well, Erik. If the left nav is showing the subpage content, I think displaying it one on page is good (there’s so little content for each of those subpages it doesn’t make good UX to break it up into truly separate pages). Thanks for the comment as well!

  52. Leave a Reply

    To include code, just include it in [code] [/code] square brackets. Sweet.