In usability we trust

Creating a Submenu in WordPress

For a project I’m currently working on I wanted to create a submenu that included the parent page as well as the supbages. I just wanted to display the submenu only if the parent page had subpages. Searching the WordPress Codex and googling for a solution I couldn’t quite find an example that took all these factors into consideration so I had to figure it out myself. Here’s the approach I came up with.

Checking if the page has subpages

I searched in vain for a method to determine if the current page has any subpages or not. I first assumed that there would be a has_subpages() method, but so far I haven’t found any. Lacking that, I came up with a very crude way of checking it. I explicitly had to try to fetch all the subpages with the function wp_list_pages() and then check if it returned anything. It’s not pretty but it works.

$children = wp_list_pages('&child_of='.$post->ID.'&echo=0');
if($children) {
    // This page has subpages
}

Checking if it’s a parent page or a subpage

The next thing I had to figure out was how to check i the current page is a parent page or a subpage. That’s done with the following code.

if(is_page() && $post->post_parent) {
    // This is a subpage
} else {
    // This a parent page
}

Fetching the submenu

Now I needed a way to fetch the subpages. This is done with the wp_list_pages() function. The tricky part about this is that there’s no way to get both the parent page and the subpages in the same call. So therefor we have to call the function twice. The call also looks a little different depending on if we’re on the parent page or on the subpage.

if(is_page() && $post->post_parent) {
    // This is a subpage
    $children = wp_list_pages("title_li=&include=".$post->post_parent ."&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->post_parent ."&echo=0");
} else if($has_subpages) {
    // This is a parent page that have subpages
    $children = wp_list_pages("title_li=&include=".$post->ID ."&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->ID ."&echo=0");
}

There are other ways of doing this, but the benefit of this approach is that we automatically get class="current_page_item" on the list-item that represents the page that we’re currently on. That’s handy if you want to style that item in any particular way.

Outputting the HTML

The last step is to output the actual HTML. I’ve chosen to output it as an unordered list.

<?php // Check to see if we have anything to output ?>
<?php if ($children) { ?>
<ul class="submenu">
    <?php echo $children; ?>
</ul>
<?php } ?>

Putting it all together

Now it’s time to put all the pieces together. Just put this code in your page template one of the pages in your template, like for example page.php or sidebar.php and you’re good to go. These pages are located in /wp-content/themes/your-theme/.

<?php
$has_subpages = false;
// Check to see if the current page has any subpages
$children = wp_list_pages('&child_of='.$post->ID.'&echo=0');
if($children) {
    $has_subpages = true;
}
// Reseting $children
$children = "";

// Fetching the right thing depending on if we're on a subpage or on a parent page (that has subpages)
if(is_page() && $post->post_parent) {
    // This is a subpage
    $children = wp_list_pages("title_li=&include=".$post->post_parent ."&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->post_parent ."&echo=0");
} else if($has_subpages) {
    // This is a parent page that have subpages
    $children = wp_list_pages("title_li=&include=".$post->ID ."&echo=0");
    $children .= wp_list_pages("title_li=&child_of=".$post->ID ."&echo=0");
}
?>
<?php // Check to see if we have anything to output ?>
<?php if ($children) { ?>
<ul class="submenu">
    <?php echo $children; ?>
</ul>
<?php } ?>

Conclusion

I think that WordPress is an absolutely awesome CMS/Blog engine, but it do lack some handy methods. Fortunately it’s almost always possible to create workarounds. I hope that you will find this useful in your own WordPress Template. Don’t hesitate to tell me if you have a smarter way of doing this.

Share this article

  • del.icio.us
  • Twitter
  • Facebook
  • LinkedIn
  • Digg
  • StumbleUpon
  • Google Bookmarks
  • Mixx
  • Print

Comments

RSS feed for comments on this post

1. March 27th, 2009 at 10.33 by Matt

You mention adding this to the template file, where in the template file do you add this? and by template file i presume you mean “template.php” in admin>includes?

2. March 27th, 2009 at 18.40 by Gabriel Svennerberg | Author comment

Hi Matt,

No I mean one of the files that makes up a template, like for example page.php or sidebar.php in /wp-content/themes/your-theme/. Sorry for being unclear about that. I’ve updated the article accordingly.

3. April 3rd, 2009 at 21.56 by Tim

Hey,

I think this is missing a <?php opening statement. And even when I add that it doesn’t appear to work.

4. April 3rd, 2009 at 22.35 by Gabriel Svennerberg | Author comment

Tim: Your’e absolutely right Tim! There was a missing php opening statement. On top of that there was also a missing php closing statement. I’m really sorry about that. I have now corrected the code in the example so please try it again.

5. April 8th, 2009 at 15.40 by Dukessa

There’s a plugin for this now :) http://www.dontdream.it/category/wordpress-menubar

6. April 8th, 2009 at 21.19 by Gabriel Svennerberg | Author comment

Dukessa: That’s sweet! If you’re not into building your own templates, that’s an excellent option. Thanks for the tip!

7. April 18th, 2009 at 11.52 by morsefire

hey dude,

thx for the nice script! i’m wandering though! my navigation is split in 2, mainNav is in the header.php and i’m calling your nice code (my subNav) into my index.php like that:

the results are, when i click on a page in the mainNav that has subNav pages (lets say 3 for example) i’m seeing actually 4 pages (the mainNav page as a subNav page and my 3 subNav pages) and that’s fine, but.

my 1st question is how could i rename the the mainNav page name that is automaticly shown in the subNav?

and, my 2nd question (the most important) is how can i get the mainNav li.current_page_itme {} to remain active when i’m clicking on another subNav pages (that is not pulled from the mainNav)?

thanks so much

marc

8. April 18th, 2009 at 12.31 by morsefire

oh,

sry for bothering! i just found the answer to the 2nd question. i needed to add the following code to my CSS.

.mainNav li.current_page_ancestor {}

uuppps :)

9. May 24th, 2009 at 15.20 by Linda

Just thought to let you know that an alternative way to do this is to use the code from the sidebar of the K2 Theme. Just strip away all the other stuff and you are left with a very nice piece of code and it’s a simple thing to make the heading clickable also if you want.

10. May 24th, 2009 at 22.09 by Gabriel Svennerberg | Author comment

Linda: Cool! I will definitely check that out. Thanks for the tip!

11. May 27th, 2009 at 19.31 by Pet Snakes

Very interesting post, I love finding a good quality blog thats not full of rubbish. I would love to do a link exchange.

12. June 19th, 2009 at 12.49 by marnix bras

Super script: Nice and well explained! Love that!
One small thing: you mention:


// Check to see if we have anything to output

And that particular text shows up in my WordPress project :-) Because it is outside a PHP tag…
Tanks for your blogpost!

13. June 20th, 2009 at 21.12 by Gabriel Svennerberg | Author comment

marnix bras: Thanks for noticing! I’ve corrected the example.

14. June 30th, 2009 at 13.12 by Cian McCoy

Hi

Great post, pretty much exactly what I was looking for, thanks for that!

I am very new to both wordpress and php so I’m sorry if this is a stupid question but I’m stuck
The code that is output from this php looks something like his for each list item:

Sub Page1

Is it possible to insert a span within the anchor somehow? so the php would output something like this?

Sub Page1

Or would this make things complicated?

15. June 30th, 2009 at 14.31 by Cian McCoy

sorry about the links in that last comment

I basically want it to output something like this (ignore the extra spaces)

Sub Page1

hope it works this time

16. June 30th, 2009 at 16.19 by Cian McCoy

Sorry for the flooding.. managed to insert a span in the anchors by using link_before and link_after like so


if(is_page() && $post->post_parent) {
// This is a subpage
$children = wp_list_pages("title_li=&include=".$post->post_parent ."&link_before=&link_after=&echo=0");
$children .= wp_list_pages("title_li=&child_of=".$post->post_parent ."&link_before=&link_after=&echo=0");
} else if($has_subpages) {
// This is a parent page that have subpages
$children = wp_list_pages("title_li=&include=".$post->ID ."&link_before=&link_after=&echo=0");
$children .= wp_list_pages("title_li=&child_of=".$post->ID ."&link_before=&link_after=&echo=0");
}

sorted!

17. July 21st, 2009 at 2.39 by Tim Dunkley

Hi, Love the work you’ve done on this little submenu code, it was a piece of cake to add to my custom theme, however I’m just wondering how it would be possible to have the submenu output on a single horizontal line? (eg submenu1 | submenu2 | submenu3 | submenu4)

Tim

18. July 21st, 2009 at 11.45 by Gabriel Svennerberg | Author comment

@Tim: Your best bet is to use CSS to style the unordered list so that it’s horizontal. A now old, but still excellent article on how to do this is CSS Design: Taming Lists at A List Apart. http://www.alistapart.com/articles/taminglists/

Hint: Try .submenu li {display: inline} in your CSS.

19. August 6th, 2009 at 18.03 by Shandilya

Can someone help me with this? I have lots of articles and I want to display them under different parents. Here is my question:

1. I want parent pages to be formatted differently than child page list

For example in the link here: http://www.theagitator.com/publications/

My parent page is called “Feature-Length Articles” [which is NOT a link and is formatted differently]

Now I want to show all the children pages as links [] list with publish date.

Thanks for all the help.

20. August 14th, 2009 at 15.46 by Silin

What about having a completely seperated and with the submenu only showing when a main page has subpages, set the submenu still shows when a subpage has been clicked.

That sounds much more difficult and is what I’m current working on.

21. August 14th, 2009 at 15.48 by Silin

WordPress scrubbed my custom divs so that post didn’t make sense. Here it is again:

“What about having a completely separated [div id="menu][/div] and [div id="submenu"][/div] with the submenu only showing when a main page has subpages, yet the submenu still shows when a subpage has been clicked.

That sounds much more difficult and is what I’m current working on.”

22. September 14th, 2009 at 23.50 by eduardo-BR

I love your script, is what a need, but all the sub-pages are showin in the search page, how can a fix this?

23. September 17th, 2009 at 9.37 by wantusiak.com » Wordpress: How to style wp_list_pages

[...] Marc Wiest was kind to share wit us a solution to this problem, based on the above hack and work by Gabriel Svennerberg. Here is how to create a dynamic [...]

24. October 24th, 2009 at 7.03 by Nate Woods

Just wanted to thank you for this, this works perfectly and was exactly what I was looking for.
Thanks again.

25. October 29th, 2009 at 18.37 by suniel

thanks, great post, works great!

26. November 23rd, 2009 at 2.35 by Lloyd

Thanks! This saved me heaps of time and worked straight out of the box. You’re a champion :)

27. January 15th, 2010 at 11.01 by Joshua Behshad

Thank you. Worked for me first time.

28. February 4th, 2010 at 23.32 by Brian

Thanks for the hack! Worked perfectly.

29. February 9th, 2010 at 12.27 by Jonas

Thanks, you helped me allot!

30. March 11th, 2010 at 0.17 by Andrew

Like many others, you’re help saved the day and I really appreciate you taking the time to share the knowledge!

Cheers!

31. April 23rd, 2010 at 5.33 by reseller hosting

I really like your writing style, its not generic and extremly long and tedious like a lot of blog posts I read, you get to the point and I really enjoy reading your articles! Oh, and merry Christmas!

32. April 26th, 2010 at 6.36 by pattaya resort

good thank you

33. May 13th, 2010 at 14.01 by David Spragu

ID.’&echo=0′);

if ($children) { ?>

I found this shorter, simpler version you might like.

34. May 18th, 2010 at 13.27 by Shell

This has worked perfectly for me, thanks! It is exactly what I have been searching for all day – after trying a lot of other suggestions thatall failed! Wish I had found this first! lol

35. June 7th, 2010 at 20.41 by Nate Tharp

Thanks so much for this Gabriel. I’ve used a few different PHP snippets for secondary menus on various WordPress sites, but none of them were able to highlight the parent menu item when that page was active. But yours fixes that. Really appreciate it.

36. June 13th, 2010 at 23.07 by Michael Gad

Hi!

I a newbie at PHP and wordpress. I used you script and it works great!

Now I have a problem though. How do I add a menulevel 3? So topmenu -> submenu ->subsubmenu

It works displaying the subsubmenu but clicking the subsubmenu the other pages in the menu dissaper.

Also i cannot figureout where i edit the for the subsubmenu.

Anyway helt would be deeply appriciated.

Thanks!

37. June 13th, 2010 at 23.08 by Michael Gad

EDIT: Also i cannot figureout where i edit the UL for the subsubmenu.

38. June 18th, 2010 at 9.33 by Eddy

Hello Gabriel Svennerberg, I am new in wordpress and I’m confused for using this now..
maybe this is weird question, but want to ask, where I can put the code that you have written it? thx for the answer.

39. August 10th, 2010 at 15.56 by How to style wp_list_pages « MalarVizhi

[...] Q2: How do you create a dynamic submenu? Marc Wiest was kind to share wit us a solution to this problem, based on the above hack and work by Gabriel Svennerberg. [...]

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>