The menu builder in WordPress is great, however quite often I find myself needing to create a secondary menu on pages, containing just a subset of the menu. In my case the rules are simple: when viewing a top level page we want to list all child pages nested beneath it. When viewing a second level page, we want to list all sibling pages nested under the same parent. We could just use wp_list_pages and filter by a parent, but it won’t match the structure designed in the menu builder.
Surprisingly there’s little information or working solutions to this problem, so I’ve put together the following code which does exactly what we want.
To give you an idea of how it works with the default options, imagine you have a menu like this:
– A
– – B
– – C
– D
If you’re on page A, you’ll see B and C in the menu (A is a top level page, so it shows children). If you’re on B or C, again, you’ll see B and C (B and C are second level pages, so it shows siblings).
A big thanks goes out to Thomas Dexter who kindly pointed out a way to use wp_nav_menu, which adds the correct wp classes and allows us to use custom walkers and the like. Using his code and the hook provided by mac joost on Stack Overflow, I created a filter that works based purely on the menu structure (irrelevant of page structure). So all you need to do is drop this in your functions.php file (remember to remove the opening <?php tag first).
Then you can display it in your theme using wp_nav_menu (just like you normally would), by passing in a sub_menu parameter to activate the custom sub_menu hook:
If you’ve copied and pasted the above code, don’t forget to change Menu Name to the slug or name of your menu.
By default, if you have many nested levels, this code will filter the menu by the absolute top level parent. If you want the menu to drill down dynamically by filtering the menu based on the direct parent, pass a “direct_parent” => true parameter to the wp_nav_menu call.
If you want to include the parent/root element in the menu, you can pass in a “show_parent” => true parameter to display the root level item.
Of course, you can continue to use the standard wp_nav_menu parameters as well. For example, if you want to limit the number of nested children being displayed, you can use the depth parameter. Or of you don’t want the menu to be displayed, you can use the echo parameter.
I hope you’ve found this post useful. If so, enjoy!
Comments are closed.
257 comments
It is very good and working fine with all options but in my side not working on my single.php any solution?
I just gets an infinite loop. What could be the problem?
Anyone know how to fix this problem ? @Christian Varga can you please help with this problem ? Solution seems to work as expected, but only one main page it simply trow errors.
Hello,
imagine that page B also has a child page, let’s say B1.
How can I still show the menu and the sub-menu on this third level page. Note that B1 is not part of any sub-menu, I get there trough a direct link on page B.
Thanks
I would also like to show the other pages on first level, how can I do that? That means when Im on page B the menu should look like:
– A
– – B
– – C
– D
The same when Im on page C or on page A.
But if Im on page D the menu should look like:
– A
– D
That means the children pages should only appear in the menu when you are on a children page (B, C) or on the parent page (A).
How can I do this?
Hey Christian, you’ll want to prevent the hook from dropping any root-level menu items. This can be done on like 43; simply change the condition to
! empty( $item->menu_item_parent )
I’ve created a gist for you here: https://gist.github.com/levymetal/c5cde99d888982205ebed75288618d80
Works perfect, thank you very much 🙂
Thanks, you saved my life and a lot of time!!
It seems to work fine. But in the article view the whole sub-menu is gone. I’m not sure why. Maybe you know why?
Is the article part of the menu?
Hi Christian,
I’m using this to show a sidebar menu, but I need to add a check for whether or not to display the sidebar. Normally, you can add ‘echo’ => false to the wp_nav_menu args to keep from echoing and use it in an if statement, but when I use this in conjunction with ‘sub_menu’ => true, the function always returns null.
Hey David, I’ve just tested this and can confirm that ‘echo’ => false used in conjunction with ‘sub_menu’ => true returns a string. I’d suggest disabling all plugins, checking your functions.php file for anything that might be hooking into the menu, and updating to/reinstalling the latest version of WordPress.
https://uploads.disquscdn.com/images/e7d6349833dc400960b01ac2f7fa0145b3e459248dba1b99bcdd5bc53fdd9cd8.png
Hey Christian,
Can you help me ? The class “page-item-XXX” is missing on all the menu item except for the current one. Do you know why and how I can add this class everywhere ?
Here the classes for the current menu item:
“menu-item menu-item-type-post_type menu-item-object-page current-menu-item page_item page-item-470 current_page_item menu-item-829”
And the classes for the other menu item:
“menu-item menu-item-type-post_type menu-item-object-page menu-item-831”
I need to add the class “page-item-XXX” on all the menu items. This class corresponds to the real ID of each element. Do you know how I can do that ?
Thanks you 😉
According to the docs, `page-item-$ID` should automatically be added to menu items that correspond to a static page. I’ve tested this and got the same result as you – the `page-item` class is only added to the current menu item. I believe this to be a bug in WordPress, so your best course of action is to submit a bug report here: https://make.wordpress.org/core/handbook/testing/reporting-bugs/
Hey Christian, thanks for writing this, I’ve been using it for a while on a couple of sites. I Have now, however, ran into a problem…
I’ve managed to use this to display 2nd level menu items in a side list here: http://www.healthwatchleicester.co.uk/newwebsite/find-services/
But, I’m now working on this website: http://new.leicesterymca.co.uk/youth-community/ Where I have the second level menu showing in the blue strip under the top level. I would now like to go down another level and show the 3rd (and actually, indented 4th) level on the side of the page… So I would just like to display a list of Cs for the top level B section…
A
-B
–C
–C
–C
A
A
I’ve read the bit above about ‘many nested levels’ but I’m not understanding it I’m afraid – Any help would be massively appreciated and a bit of a life saver 🙂
Hmm, this is quite a tricky one to solve. The `direct_parent` flag could be modified – instead of passing a boolean, you would pass an integer to specify the level you wanted to display. However, in this case, I think CSS will provide an easier solution.
.sub-nav-left .menu-level-2:not(.current-menu-item):not(.current-menu-ancestor) { display: none }
Thanks for this Christian, It didn’t work just pasting it in, but ill play around with it and see if I can get it working – Thanks for taking the time 🙂
I tested this by injecting it directly into a style tag in the dom, and it works on all child and grandchild pages of youth-comminuty. Here’s a video to show what I did: https://youtu.be/XYFAtOjMUuE. Where did you put this CSS? Did you clear your cache after including it?
After a bit more testing, one issue I noticed is that on the root youth-community page, nothing is displayed. The rules need to be changed on this specific page, and this is where the CSS solution starts to fall apart. You could do something like this, but referencing the page-id in CSS is a very poor solution.
body:not(.page-id-38) .sub-nav-left .menu-level-2:not(.current-menu-item):not(.current-menu-ancestor) { display: none }
The best solution would be to modify the wp_nav_menu hook, but this isn’t something I’ll be able to look at for quite a while.
Hi Christian, hope you’re still taking comments on this. Thanks for the code and subsequent dev fixes. I’m learning plenty from studying it.
My question is around the output. The function returns entries but the items between the tags are all empty! Have you seen this before?
I built a test site with the same theme (Kadence Virtue) and activated all plugins, but cannot replicate the fault. My only hope is that you have come across this in the past and remember the cause!
Very many thanks.
Phil.
Unfortunately I haven’t come across that before. If you’ve created a test site and it doesn’t occur there, then something in your installation is affecting the output. Have you added any extra code to functions.php? Compare your functions.php between the two sites. You could also try working backwards – deactivate all plugins and theme, then start activating things until the problem comes back. That’ll pinpoint exactly where the issue is. Don’t forget to make a backup of your site first though.
The demo❶ is not active anymore, is it possible to get the demo works again?Thanks.
❶: http://wp-sub-menu-demo.christianvarga.com/
Any news?
Hi Ben, unfortunately the demo is not available any more. It didn’t show anything other than what’s described in the article.
I see, thank you for your attention.
This submenu does not show on single.php file (single post template). Is there any way I can have it show up on this templates too?
The submenu works on all templates, including single.php.
Keep in mind that the page/post must be in the menu for the submenu to work properly. The code works by filtering the menu based on the current menu item. If the page/post isn’t in the menu, it won’t ever be the current menu item, so it can’t show a submenu.
If you’re looking for a solution where the page/post isn’t in the menu, but you want a submenu to show, you’ll need to hack the filter. Someone has already done this, however all the comments on this article seem to have been deleted. I’ll try and get them restored, check back in a few days.
Thank You for your prompt response. Sorry, I’m not very good at programming. My post is linked to the category which is in the menu. The submenu works in the category pages with that post, but not in the post single page.
https://uploads.disquscdn.com/images/74630ffc3608de428dd7c39a9c92ac587115e4b54e3d8e401a9edccceca702d2.jpg
Yep, that’s happening because the post isn’t in the menu. The submenu will only be displayed on pages/posts that are in the menu.
My filter isn’t designed to work with categories. It’s quite tricky to modify it to do what you want. At minimum, you’d need to:
1. Check if the active/current post is within a category
2. If so, check if that category is in the menu
3. If so, fake the root_id in the filter to the menu_item_id of that category (not the category id)
I’ve managed to reinstate the comments on this article. I don’t have time to read through them all right now, but there might be a solution somewhere.
If not, your problem seems like a common one, and I’m sure many people have solved this before. Perhaps if you keep searching you’ll find a plugin that can do this.
Ok, thank you!
thank you
This is a perfect solution! I am mostly mad at WP-dev team that makes this so troublesome.
I also need a similar solution to this – tried for hours – to no avail.
I need to have all “menu-item-IDs’ in an array, for the submenu – to make some neat stuff that i need to do.
Hi,
a quick question, is there some simple way to mark the selected item in the sub menu? Thanks!
Because this code uses wp_nav_menu(), it will apply the default menu item css classes such as `current-menu-item` to the active page. For more information on the css classes that wp_nav_menu() adds to the menu items, you can refer to the WordPress code reference: https://developer.wordpress.org/reference/functions/wp_nav_menu/#menu-item-css-classes
Had a quick question. I just added the code to my site and work great except for one issue. My structure is currently
home
– test
– contact
– test 2
Contact
– test 1
This issue is contact under home and contact (parent page) are the same page. When I go to the contact (parent page) it displays the home sub menu because it is what comes first in the menu structure instead of the contact (parent page) structure. Is there a quick way to fix this so that no matter where in the menu an Item is placed if it is a top level nav item it will always display its own sub menu? I have direct parent and show parent both set to true and have played with the combination of them with no luck. Thanks
You’d need to change the first foreach loop (the one that finds the current menu item) to prevent it from returning the parent if the current menu item exists as a top level item itself. This shouldn’t be too hard to solve, but unfortunately I don’t have time to solve it.
You’ll notice a break statement in this loop (line 16). If you comment out this line, it should be a quick fix for your issue. Instead of returning after the first Contact menu item is found, it’ll continue to go through the loop, find the second Contact menu item, and set that as the root. However, this hack wouldn’t work if your menu was the other way around.
Useful piece . Just to add my thoughts , if you is searching for a service to merge are interested in merging of , my colleague found a service here
http://goo.gl/E1xqPh
Very nice but i stiil have an issue for this example :
-A
–B
–C
—D
—E
When i’m on page C, the displayed menu is “B, C” and i would like to display children, so “D, E”
Edit : Just to precise that C is a link not a page so i think there is no trick to do with wp_list_pages or i don’t know how
Edit edit (Damn i’m a faggot) : In fact i’m wrong on the first edit, when it’s a link it’s working because my link redirect to the first sub item but the problem is when C is a page
I’ve had a think about this, but it’s a tricky one to solve. The functionality has to change depending on what page you’re viewing – on C you want a child-only menu, and on D/E you want a sibling-only menu. One solution is to add a new option called `children_only`. So on page C, you’d call the menu with this option to hide the siblings and only show the children. But on page D and E you’d need to call the menu like you currently do now. So you’d need to add some logic to your theme, or have a different page template to give you the flexibility to change the parameters depending on the page. If you think this will work for you, let me know, and I’ll see if I can add the `children_only` option to my hook.
Well, it’s a good idea and i think i’m not the only one in is case. In fact i’ve already tested some wp_page_list or wp_nav_menu and for the moment both have useful way like for your hook, possibility to display a custom link with link of pages that the wp_page_list can’t do because it just take “page” or “post” and not custom link. And for the wp_page_list there is the option “child_of”.
So yes i think it will be a good idea to add the option children_only (compatible with show_parent for the title of the menu)
And why should i change my template or other things ? Can’t you just check if a nav menu items has child so you display child and if not you display siblings ? So the option will not be children_only but show_children maybe ?
PS : Sorry for your eyes and my bad english (i’m a french fag)
If a `children_only` option works for you, then by all means you can add that logic to your own version of the hook.
In your example, B and C are on the same level but would have different menus because one has children and one doesn’t. So that’s something to keep in mind. Most people want the menu to be consistent based on the level of the menu item.
Ok, i understand but when you have multiple submenu with multiple items, for myself i think that is a bit easier to read then there is not a busy menu like seven items on top level and seven in many submenu of the top level etc etc… Don’t know if i explain clearly but give me your thinking about this case, for you what is the most clearly or useful way to display a menu
That’s totally fine. Everyone’s website is different so a menu structure that make sense on one website might not make sense on another. In your case, based on what you’ve described and the way you’ve structured your menu, it should be fine.
Ok, so i don’t really know how to create this child_only but i think it’s a kind of reverse search from the bottom of the tree (the menu) and check level by level if the parent item is the current item. Something like that ?
You don’t really need to check level by level, you just store the current menu item ID, then loop through all the menu items again and check if any of their menu_item_parent matches the current ID. If so, you know the current menu item has children, so you can set the root_id to the current menu item.
For example: https://gist.github.com/levymetal/d6d76c0988f786cdffe1615995b14cfb
Well ! This works, thanks a lot !
Thanks again for all this! Question: how would you then either combine this new gist with the pre-existing ‘// find the top level parent’ code, or create a new hook and filter with the ‘children_only’ option without overwriting the existing function? Basically what I’m attempting is to always display siblings regardless of 1st/2nd/3rd level in the menu. However, if there are children of that page in the menu, display them but only when you’re on that particular page.
Hi, really great code, but I need to be able to keep parent item in the navigation too, could anybody give me some pointers cheers
Don’t forget to thoroughly read the post before commenting. “If you want to include the parent/root element in the menu, you can pass in a “show_parent” => true flag to display the root level item as well.”
awesome. just what I needed. Used this one – but gave me problems: http://stackoverflow.com/questions/5770884/how-to-display-part-of-a-menu-tree
Hi, great code, it would be very nice if you can make it as a plugin, for none code savvy to use. Also user can add custom menu in widget manager and have all the option in dropdown selection. Way to go!
Hi, is it possible to show subitems only when parent is activated.
If A is clicked, then it show BCF, but when C is clicked, then it shows BCDEF? Is it possible?
A
-B
-C
–D
–E
-F
on single.php it’s not working, even I passed parameter ‘direct_parent’ => true
The code works fine on single.php. I even provided a link to a demo which shows it working in such a case. Please re-read my post and ensure you’re using the code properly, and please make sure that you understand what the code actually does. If you are sure you’re using it properly and you understand what it does, then you have a plugin that’s conflicting with my code. Obviously, if you don’t provide any specific information, then there’s nothing I nor anyone else can do to help you further. The code does work, so telling me that it doesn’t work doesn’t provide me with any useful information that I can use to diagnose the issue. This article might be of interest to you: http://mattgemmell.com/what-have-you-tried/
Thanks for the reply. I am now trying to figure out thought the code and url, mean while it’s absolutely working fine on category page. The category is listed in primary “menu” and each category have sub category ie “secondary”.
like
a => category (primary menu)
-a1 => category (secondary menu)
-a2
-…
b
-b1
-b2
-…
c
-c1
-c2
-…
Dunno whats going wrong.
My code only works if the post is in the menu. It doesn’t contain any logic relating to categories, which means that if the post itself is not in the menu then the submenu will not work as you expect. What you need is code that checks the category on the post, and displays the menu for that category. My code doesn’t do anything like that, and it would be quite hard to modify my function to do what you want. You might actually have more luck with wp_list_categories.
hi there.
how can I show latest post per tag in the menu. I am trying to build a mega menu that will show latest post title and thumbnail when you hover a submenu. thanks for guiding me in the right direction.
Good job, works great, you saved me from few hours of work ! 🙂
Thanx !
Not often I see a post like this that I think should be core WP. Well done mate, worked a treat for me.
Glad it helped!
Hi, I have been strubbling with this menu problem for a while now and can’t seem to get the answer I am looking for. I would like to display all the “sibling” menu items only on the sidebar. So if I have the following menu structure;
– 1
— 1.1
— 1.2
— 1.3
— 1.4
— 1.5
– 2
— 2.1 …….
— 2.5
– 3
if the user is on the page/post 1.3, I would like to list
1.1
1.2
1.3
1.4
1.5
..only. Currently, I am able to list the siblings with a function I have, but only if I hard-code the title of the page 1 which is the ancestor of 1.3. I would like to get the name of the ancestor of 1.3 dynamically and not have to hard-code it. How can I ;
1. dynamically get the immediate ancestor of the page I am in?
2.If question 1 is not possible, how can i get the siblings Only?
Did you read my post thoroughly? I developed the script in my post to solve the exact issue your having. Have you tried to use it? If so, what was the result?
I had tried, but it didn’t give me what I expected. So I resolved to a different solution. I was trying to filter children of a menu item, I scrapped that and created child pages instead. This was much easier. My plan was to traverse the menu created.
Is it possible to copy the menu from a specific page onto a different page if it doesn’t exist in the menu structure? I’ll try to explain better:
I’m using The Events Calendar Pro plugin and it generates pages for each of the events. I have the main page for the Events calendar setup in my menu structure so the first page has the menu appear, but not on any of the single event pages. Is it possible to have the menu also show up on the single event pages without including each page in the menu structure?
Sorry if this is the wrong place to post this.
Another reader had a similar problem, if you scroll down in the comments and look for a post from robmaurizi you’ll see his solution. I also wrote a function that accepts a root_id parameter, which essentially “fakes” the current page and allows you to force a specific sub-menu to be displayed. You’d just need to find the menu_item_id for the main event calendar page (you can do this by inspecting the source code of the page), then use this function and pass in the root_id parameter: https://gist.github.com/levymetal/37e7a0853bbe410b9f57
Wow, I never received an email notifying me that you replied. Luckily I checked this a few days ago. Thanks so much for developing that modified function. It worked perfectly, but I have to admit I was stumped for a bit because I didn’t realize that I needed to use solely the menu item id number. Once I just put 389 for the root id everything displayed properly. Thanks again.
Yeah it was a bit hard to explain, I probably should have provided an example. I’m glad you got it figured out anyway! Thanks for your kind donation 🙂
Wondering if you could advise if you need to force 2 root_ids on different pages?
It depends on what you’re trying to do. Forcing the root_id basically makes WordPress think it’s on a different page (so it displays the menu for that page). if you wanted the same menu to appear on every single page of your site, you’d always force the same root_id (which would be the menu_item_id of that page). If you wanted different menus to be displayed on different pages, then you’d need to force a different root_id depending on the page.
yes, I think I want to force different root id depended on pages. Would I pass an if else if statement for wp_nav_menu and leave root id in the functions.php to 0 or would I be changing functions.php file as well. I’m running into problems when I have a single page whether a custom post type single or just a page that is not included into the menu or is not a child page. For example, having multiple single custom post type pages (that are not part of the root but you want to different sub menus), Wondering the best way to handle those types of pages without creating an additional sub menu and having to maintain that as well. Thanks for the help!! great work.. I’ll be sure to donate.
It’s a bit tricky to answer your question without actually looking at your codebase, but I’ll try anyway. I would recommend using this version of my function: https://gist.github.com/levymetal/37e7a0853bbe410b9f57. Using something like the Advanced Custom Fields plugin, you can add a custom field to your custom post type called root_id. This will allow you to manually set the root_id for each of your posts (you can also set a default value in ACF). After that, all you need to do is pass that in with your call to wp_nav_menu, like so: `wp_nav_menu( array( ‘theme_location’ => ‘primary’, ‘sub_menu’ => true, ‘root_id’ => get_field( ‘root_id’ ) ) );`. I hope this puts you on the right track.
I found the other version of your function, which works great for what i needed. https://gist.github.com/levyme…. Using ACF is great idea. Thanks for you’re help and advice.
If you’re reading this, you should be figuring out how to do it with a custom nav walker, grabbing the second level for only items containing the classes: “current-menu-item”, “current-menu-parent”, “current-menu-ancestor”
Please help me figure something out with it. Can this be modified to add menu items of another wp menu if item name equals to menu id. I’ve been looking for a solution to divide mega large menu into smaller sub menus, and publish them dynamically when item name of primary menu matches to menu id. For example, if I have the primary wp_nav_menu structure of
– 1
— 1.1 …….
— 1.5
– 2
— 2.1 …….
— 2.5
– 3
and many other menus with id : 1.1 ; 1.2 ; 1.3 ……….. 2.5
It might be a little tricky to do that with my solution. You should look into wp_get_nav_menu_items. Using this method, you can get an array of all the items in your menu. You can then loop through your array, which will give you access to the title parameter (eg, 1.1, 1.5 etc) and then you can make a call to wp_nav_menu using that title to retrieve the menu with that name. Give it a shot and see how you go: http://codex.wordpress.org/Function_Reference/wp_get_nav_menu_items
Thanks so much for quick reply. But do you have any close examples to what I am looking for? I’ve gone through references but I’m not so good on php. 😉
Unfortunately I don’t have any actual examples, what you’re looking for is pretty unique so it might be hard to find a plug-n-play solution.
At the moment I dont have the time to write a complete custom solution for you, give it a go yourself (learning php is always a good skill) or perhaps head over to the WordPress forums / Stackexchange and see if you can get any help there.
Yeap, working on it. Thanks anyways )
so if my menu 1.1 will be sort of this
-a
– – a.1
-b
then result should be –
– 1
— 1.1
-a
– – a.1
-b
…….
— 1.5
– 2
— 2.1 …….
— 2.5
– 3
I really hope you can help me figure something out with it. Can this be modified to only show the child pages, excluding the parent page and the parent pages siblings? For example, if I have the wp_nav_menu structure of
– Example Page
— Test Page
— Another Page
If I’m on Example Page, or any child pages of Example Page, I need to show just the sub-pages, not the parent level.
EDIT: I just realized I’ve posted here before, haha. This is great stuff, I’ve used it so many times 🙂
So using direct_parent = true seems to work okay on the second level pages, but not on the third level or lower, and not on the first level.
On the first level (i.e. Example Page above), I need to display only the children (and possibly sub-children, based on the depth argument), of that current page. Here, I can use a separate call for the wp_nav_menu if necessary.
One the third level and deeper (i.e. pages below Test Page or Another Page above), I need to display the same menu as on Example Page.
I was able to use the parent_id parameter from a question below to *almost* achieve what I’m looking for. The only problem is that it’s only displaying one level of sub-menu now for some reason.
https://christianvarga.com/how-to-get-submenu-items-from-a-wordpress-menu-based-on-parent-or-sibling/#comment-1660393399
I figured out a solution that involves treating first-level menu items as root-level items. Try the code at the following gist (note, don’t use the direct_parent parameter): https://gist.github.com/levymetal/422c56120964a05ff31b
I’ve been looking into this but it seems a little tricky to solve. I’ll keep thinking about it, and I’ll let you know if I come up with a solution.
what about special pages like search, 404 and archive views. The do not have a natural menu parent, and so will leave the sub menu empty.
If there aren’t any child or sibling pages to display, there aren’t any child or sibling pages to display. I don’t get what you’re implying. If you want to display a different menu when the sub menu is empty, there is a comment just 6 messages below that describes a way to output a default menu if no sub menu items are found.
Omg, I “dropped” the code in the functions.php just like you said and now my whole wp project has gone totally blank! I cannot access any page any more.
desperate
Thx for your help.
Tina
If it broke your site, did you try removing the code? Removing the code will fix your site. It sounds like you may have copied the opening php bracket.
Dear Christian,
there is no more accessing the site, it is totally blank, both the front and the backoffice admin! So no access no removing the code.
The opening php bracket is not supposed to be copied?? Maybe you should say so in your explanation. Why is it in the code if it isn´t supposed to be copied and you say “just drop this code”…
The opening bracket is there for code formatting. This is a tech blog, so I assume that someone who is willing to edit their functions.php file knows a little bit about php and WordPress. There are many other reasons it could break, perhaps a conflicting plugin, or an incompatible version of WordPress. It’s impossible for me to guarantee that it won’t break your site; if you’re inserting random pieces of code into your site then the onus is on you to know how to remove them if they cause issues.
You need to log into your site via FTP, navigate to your theme (something like /public_html/wp-content/themes/yourthemename/), download the file, edit it, and re-upload it. I’m sure there are a plenty of tutorials on the internet about accessing your site via FTP so I don’t really need to cover it here.
Hi. I can’t figure out if I misunderstood how to plugins works or not. I want to filter and show a specific portion of the menu. Is this possible?
Have you actually tried to use this code to see if it works for you? Your description doesn’t give me enough information to figure out whether it will work for you or not. In my article I provided a link to a demo to show how it works: http://wp-sub-menu-demo.christianvarga.com/locations/europe/portugal/. If you use the ‘sub_menu’ => true and ‘direct_parent’ => true parameter, it will only show that level of the menu, as you can see in the second two examples on the page I just linked you to.
Hi Christian. Maybe I misunderstood how the plugins works. Does it work only for parent->child hierarchy pages?
I have a custom mixed menu. For example:
– About us (page link)
— Staff (page link)
— People saying (page link)
– Works (category link)
– – Tools (page link)
This is fantastic. Is there a way to output the top level list items if no sub items are found?
Okay, so it’s not the most elegant solution in the world, but here’s how I solved this…
ob_start();
wp_nav_menu(array(
“container” => false,
“items_wrap” => “%3$s“,
“sub_menu” => true,
“theme_location” => “primary”,
));
$sub_menu = ob_get_contents();
ob_end_clean();
if (!$sub_menu == “”) {
echo $sub_menu;
} else {
wp_nav_menu(array(
“container” => false,
“depth” => 1,
“items_wrap” => “%3$s“,
“theme_location” => “primary”,
));
}
Yeah that will work fine. If you want to avoid using the ob_ functions, you can utilise the echo parameter of wp_nav_menu instead. For example, take this gist: https://gist.github.com/levymetal/7dc5e1aa0a737ed1ba24. Chuck that in your functions.php file, then anywhere in your theme you can simply call my_wp_nav_menu().
Thank you so much!! This code is really a lifesave like robmaurizi said 🙂
This code’s a lifesaver and is being integrated into 2 projects I’m working on right now… Great bit of work all around!
Question: Is there any way you can think of that I can pass a parent page or (or even menu item) ID to this function to get back that sub-menu? The trouble I’m having is with custom post type single templates that APPEAR to be children of a page in my menu. But because the post isn’t in the menu, the sub-menu isn’t generated.
I’m thinking if there’s a way to force the function to think it’s on a given page (the parent page in this question), it can generate that sub-menu. Basically spoofing the $root_id or something, but I’m having trouble following exactly where or how that would need to happen.
Thanks!
-Rob
Huzzah! After a little digging and tinkering, I came up with a solution. I put this before the final loop through the sorted_items array on line 35 of your example above. Simply pass a parent_id argument to the menu to force a sub_menu of a given page. Note, that it’s the Page ID (which you’re more likely to have available) than the Menu Item ID (which is just the DB row ID for that post). This would work for anyone looking to keep a menu up on posts, etc., where WP falls out of the menu.
Enjoy!
-Rob
if (isset($args->parent_id)) {
// $args->parent_id is the ID of the page whose submenu we want...
$_parent = 0;
$_sorted_copy = $sorted_menu_items;
foreach($_sorted_copy as $key=>$item) {
if ($item->object_id == $args->parent_id) {
// this is the top level.. save this value for a moment.
$_parent = $item->ID;
break;
}
}
foreach($_sorted_copy as $key=>$item) {
if ($item->menu_item_parent != $_parent) {
// this item doesnt belong to the parent, so dump it
unset($_sorted_copy[$key]);
}
}
return $_sorted_copy;
}
Rob, thanks so much for your kind donation and posting your solution here. I’m pretty sure there have been other people looking to do what you wanted to do, so I have no doubt that this will be of use to others as well. Cheers!
So I’m trying to use this code, but I’m having a hard time getting it to work right. It lets me set the parent, but it then breaks the depth of the menu. It’ll only display one depth.
If you’re happy to use the ID of the menu item, you can simply pass in an extra root_id parameter and bypass the root_id calculations. You can find the ID of a menu item by inspecting the source code that wp_nav_menu spits out. For example: https://gist.github.com/levymetal/37e7a0853bbe410b9f57. If you want to use the page_id instead of the menu_item_id, you would need to loop through each of the sorted menu items, check if the object_id matches the page_id you want, and if so, set the root_id to the ID of that menu item.
Finally got around to integrating this in to a site (I used a workaround for the original site that I asked for). Here’s my code, works perfectly:
http://git.ghostlyco.de/snippets/12
Thanks a lot for this great work Christian ;-).
I have only one trivial question, is there an easy way to avoid the parent menu? Only show the current level you are in.
Again, thanks for the code 🙂
Did you try adding the “direct_parent” => true flag to your call to wp_nav_menu? Check out the demo and scroll down to the second last example, I’m pretty sure that’s what you’re after. http://wp-sub-menu-demo.christianvarga.com/locations/australasia/australia/
Sorry, I didnt, and I thought i read trough it all looking for an option like that!
Thanks again!
Awesome, it’s a pretty long post and easy to miss as I didn’t put it in a code snippet. Also my explanation of what it does isn’t that clear either. Glad it worked for you!
cant get help .. i want to make sub menu under nav-menu but i can’t .how i can make ?
Question: is there a way to use your method to get those menu items as objects instead of printing them on screen? Or maybe I can use the walker with wp_get_nav_menu_items with few modifications? (to be honest, I didn’t have the energy to look into native wp_nav_menu function and to try to modify it’s behaviour – I did solve my problem, getting current menu item children as objects in a different way, by filtering all menu items array based on current menu item id – i think it’s pretty much same as walker, just run in different order). Does walker approach bring any extra benefits then my method?
… and as Oliver posted .. it’ seems wierd to me that with all functionality WordPress offers it doesn’t have methods to select items placed as one of combinations appearance menu allows as to build (using menu is much simpler then using parent dropdown for every item, + it combines different post types).
I originally wrote a function that generated the menu manually, probably similar to what you’ve developed yourself: https://gist.github.com/levymetal/5375456. Although I don’t know exactly what it is that you need to do, I’d chance a guess and say it probably is possible with a custom walker.
The real benefit of my hooked approach is that I can post the code online and people can use it straight away without any modification. Because it hooks into wp_nav_menu_objects, it can be used with wp_nav_menu (which every theme uses to generate a menu), and even works with a custom walker. My original approach of generating the menu manually didn’t hook into wp_nav_menu so the classes were all wrong, the html output was different, and it couldn’t be filtered with any of the options avaialble to wp_nav_menu. So it wasn’t really a plug-n-play solution like my hooked approach. But if it’s for your own personal project then there’s little difference. If what you’ve written works then there’s no real need to change it.
Thank you so much for this code. I hope WordPress will implement similar feature to their wp_nav_menu.
Thank you very much Christian! It really helped 🙂
Thank you for the code! it just saved me in a project I was handling.
Thanks so much for this. I use this functionality on so many of my sites I decided to make a plugin for it today. Your code’s given me nearly my whole plugin, so I’ve just sent you a virtual beer. I’ll post back with the plugin details in the future if I can polish it off and make it fit for public consumption. Thanks again!
Thanks
Great post Christian. A clean and beautiful solution.
THANKYOU!!!!
This saved me so much time… I’ve been coding individual custom wordpress menus for each area of the website…. DUHHHH!
Hey Christian, thank you for your snippet, it helped me greatly. I have a little issue though: I want to show the parent page menu item as well. This works with the show_parent => true setting, but I need the parent and the children elements on the same level. Not sure if I worded this correctly, I want an with elements, be it the parent or child and no other sub-navigation . Basically children elements should become siblings to the parent. Is there a way to do this?
Thanks in advance
The thing about my script is that it’s simply a filter for wp_nav_menu; it doesn’t change the structure of wp_nav_menu nor does it allow you to change the structure. It is merely filtering the items that wp_nav_menu displays; wp_nav_menu is still handling the output.
In your case I’m not quite sure how you’d go about it, but your first step would be looking at how to flatten wp_nav_menu. If you take a look at the docs (click here), it says that by passing in a depth parameter of -1 it will display the results in a flat list. I have not tried this out myself, but it’s a good place to start.
Alternatively, if the only problem you have is styling, you could simply use CSS to style both the ULs and LIs the same, which would make it look exactly the same as a flat list.
Ok, thank you for your quick answer, I’m going to look into these.
Just wanted to let you, and the others know that the depth => -1 wp_nav_menu setting indeed worked, so thank you again for your help.
Good to know!
I found this widget https://wordpress.org/plugins/advanced-menu-widget/ really useful for this purpose. Very easy to use too
I don’t know if you have stumbled on this before or not, but if you are using the Advanced Navigation Menu plugin, in conjunction with this script you can sometimes run into an issue where you get an empty li at the beginning of your outputted sub menu.
I found that this is because Advanced Navigation Menu also has an add_filter hook for ‘wp_nav_menu_objects’. It is using the same priority level as your code, but seems to trigger afterwards. I didn’t delve into it much further to determine why it adds the blank li, but by bumping your filter hook up to 100 instead of 10 it fires afterwards and seems to resolve the issue.
Hope this helps someone out there.
Cheers
Thanks for commenting Jeremy, if you don’t mind I’ll add this information into the next edit just to make sure that people see it if they come across the same problem.
Yeah, go for it. Glad to help.
Hello Christian,
great job, but I have a problem:
wp_nav_menu (array (‘theme_location’ => ‘primary’, ‘sub_menu’ => true)); work correctly in the category but does not work (not visible) in the post pages, can you help?
By “post pages” do you mean posts or pages? This function is specifically designed to work with the structure designed in the menu builder. If your post or page doesn’t exist in the menu, then this function won’t display anything. It will only display items that exist in the menu. This function is designed for use with nested pages, not for posts. If you wanted this menu to work for posts, you’d have to add each post to the menu, which is very redundant and not dynamic at all. If you are trying to display post categories and posts, have a look into wp_list_categories() instead.
sorry, I think I have not explained well
I have a menu with 2 levels (parents and children), and I split the menu in two different divs in two different positions. The first menu has only parent pages, the second menu displays all the children of the category in which we are now.
When I go to the page I would like the children of the menu is visible but disappears
To be honest, no amount of explaining will help me understand the problem you’re having. Without seeing your code, it’s virtually impossible for me to be able to diagnose and fix the problem. There are literally thousands of factors that could be causing this to happen, assuming you’re using my code in the way it was intended (which is unclear anyway). The only way I can help is for you to provide me with full access to your codebase on a working demo of the site. Without access there’s simply no way I’ll ever be able to figure out your problem.
I put your function in function.php
In header.php i have:
‘primary’, ‘depth’ => ‘1’) ); ?>
‘primary’,’sub_menu’ => true, ‘menu_class’ => ‘sub-menu’) ); ?>
Works: In index.php i see only main menu
Works: In category.php i see two menu main and secondary (see only subcategories from parent selected)
Don’t Work: In page.php , i see only main menu but don’t see secondary menu.
Your script works like this? I can send you the template?
Everything in my previous message still stands. Think of it like this: your car has broken down and you call up the mechanic to ask him what’s wrong. When he asks you for information, the only information you give him is that the car has an engine. Sure, the mechanic could then spend 10 hours describing every single one of the thousands of problems that could possibly cause the car to stop working, but that’s not an efficient use of his time when he could just take a look at the car and find the problem within 10 minutes.
Showing me the code you use to call the menu is like saying the car has an engine. That call obviously works for me and hundreds of other people, so it’s not where the problem lies (and yes, it does work on page.php, check out the demo: http://wp-sub-menu-demo.christianvarga.com/)
Sending me your page template is like sending me the cars manual. That’s not going to help me figure out the problem either.
Please don’t take offence to this, but I did not write this blog article to then spend hours upon hours of my time diagnosing bugs on other peoples websites for free in the most difficult way possible. I am happy to help, but I need you to help me as well. I need you to be respectful of my time and provide me with everything I need to diagnose the bug, so that I don’t have to spend hours trying to work out every possible option in my head.
The only way I can help is if you set up a demo site and give me access to both the codebase and wp-admin.
Great piece of code! Just one question… I have a third level on my menu, and I want my submenu to be shown for these pages too.
So my page is like this:
Marknad
– Sponsorer
– – Huvudsponsorer
The menu appears both on “Marknad” which is parent page, and it appears on “Sponsorer”, but when I click my way to “Huvudsponsorer” the menu goes away.
Can you solve this with some code of yours? If you can make this work your god man.
Anyway, thanks for a very good piece of code!
Hey mate, the code definitely works for an infinite number of sub-levels. Are you sure that Huvudsponsorer is using the same page template that makes the call to the menu? Or have you modified your call to wp_nav_menu() in any way (eg, depth parameter)? I’ve created a demo to show how the different options affect the output, but as you can see, the nested pages still display the menu: http://wp-sub-menu-demo.christianvarga.com/. You might need to try and isolate the problem by disabling any plugins and trying the code on a different theme (in case the theme is modifying the menu in any way).
Thanks for the reply. What I need is something like this:
“Marknad” is visible on all top levels, and always. This is first menu-item.
Then under “Marknad” I have “Sponsorer” that is a child to “Marknad”. And under “Sponsorer” I have childs like “Huvudsponsorer” and “Centrala Sponsorer”. When I’m on this level I still want to have the parent meny available. So Marknad is still visible, but the menu with “Sponsorer” dissapear. That second level should be visible on all childs.
Have I make myself more clear or am I unclear or just stupid? 🙂 I can paste a livesite where I use the code and you can see the problem yourself maybe. 🙂
Did you check the demo I linked you to? From your description, I still believe that my code already does exactly what you want. However, I don’t understand what you mean by “So Marknad is still visible, but the menu with “Sponsorer” dissapear”. That doesn’t make sense. Perhaps instead of trying to explain what you want the menu to look like with words, just show me the exact structure you expect to see on each page.
Hi! Check this page: http://khk.se.turbo.i8t.com/marknad/
Everything works fine if you click around on the different pages, but if you click on “Våra sponsorer” and then in the sidebar go to a child of this page, the menu will disappear.
Should I structure my menu in menusettings diffrent?
Hey mate, unfortunately I’m still not sure what the problem is. The menu in the sidebar does not disappear when I click on any of the children pages of Våra sponsorer. Have you already managed to fix the problem?
Hi! Yes I managed to fix the problem myself. Probably my own fault from the start. The code I ended up using was this: ‘primary’, ‘sub_menu’ => true, ‘show_parent’ => true, ‘show_parent’ => true)); ?>
Ah ok, glad you got it sorted. Just after I read your post I decided to make a demo because it’s not obvious what all the different options do until you actually test them out yourself. But the demo I made doesn’t really explain it very well either. I’ll work on it to see if I can make everything a little more clear 🙂
Hey Christian,
thanks for sharing – works great! I just want to do something that i could not find in the discussion.
I want to display the submenu wrappers only if the submenu exists. It seems like i can’t do it with has_nav_menu() because it checks for primary-menu that always exists.
Is there another way to check if there actually is a submenu or to define the wrappers within the functions.php?
I need to wrap it like that:
‘menu_order’, ‘menu_class’ => ‘nav’, ‘theme_location’ => ‘primary-menu’, ‘sub_menu’ => true) ); ?>
Thanks again for sharing that simple solution.
Best
Joe
Actually you can define the wrappers in your call to wp_nav_menu using the ‘items_wrap’ parameter (docs). For example, add this to your call to wp_nav_menu instead of defining the wrapper outside of the call: ‘items_wrap’ => ‘%3$s’
do i put it like this or i need to change something
because i try it and the probleme when i stay hover on corporate after the submenu close
can you help me ?
gracias
I would recommend heading over to stackoverflow.com and asking a question there. I’m quite full with work right now so I don’t really have the time to go into your website and fix all the problems with it for free.
hi i have the same problem in my website http://www.clisho.com/dante/
how i can change the code for when you click on corporate profile you go to the page and the submenu stay open
please some help
Technically this isn’t really the “same” problem as you’ve already got a submenu there, and I don’t even think you’re using my code. You just need the submenu to stay open when the link is activated.
Unfortunately whatever menu script you’re using is setting the display property using javascript, which is bad practice and also means the only way to override it with CSS is to use !important (and I *hate* !important). But if you put the following code into your stylesheet, it will work:
.current-menu-item .sub-menu.sub { display: block !important; }
Great bit of code.
Thank you! This was exactly what I was looking for. Surprising this is not built into wordpress
YES! So rarely can you just drop code in a template, hit refresh, & it work. Thank you!
Hi! First off, Thanks so much for this. This is working great!
I had a quick question you might be able to help me with. I have investigated this, and the inspiration from the StackOverflow article you posted as well, but can’t seem to come up with a good solution.
I want to display the submenu, as well as its direct parent item (but not its parent’s siblings).
Consider this menu structure:
Home
About us
— Board of Directors
— — board member A
— — board member B
— Solutions
— — Solution 1
— — Solution 2
When I am on the “Board of Directors” page, with your above code, I get the branch:
board member A
board member B
Which is 99.99% of what I want. I was curious if it is possible to also include the parent of this branch, so that the output would be:
Board of Directors
— board member A
— board member B
I get very close with the version supplied on stackoverflow, when I use $this_post->parent_post->parent_post, as that gives me the grandparent of ‘this’ node, which I was hoping would snip itself, and only show its children. The downside is that I end up with something like (which makes sense):
Board of Directors
— board member A
— board member B
Solutions
— Solution 1
— Solution 2
So I would like to exclude the Solutions branch from being all together (unless, of course, I am on the solutions page).
It seems I am so close to solving this issue, but I can’t seem to get it right.
If all else fails, I will try to use the SO version, and yank the unwanted items from the DOM with JS, or hide them with CSS (both solutions I would prefer to avoid).
Any help would be welcome! Thanks!
Hey Andrew, I’d personally echo the correct page title above the menu in the theme instead of using wp_nav_menu. Having said that, if you do want to use wp_nav_menu to do it all, you could change the else statement on line 43 to something like this: else if ( ! in_array( $item->ID, $menu_item_parents ) ) {, which would continue to filter the menu like it currently does, except it will skip removing the parent item from the tree. It needs to be done this way because as you’ve already noticed, if you try filtering by the parent, you’ll actually end up with all of the parent’s siblings (and their children) as well.
Beauty!
Works just as I want it to!
I needed this subtle nuance from wp_nav_menu because the nav title and the page title differ slightly from time to time.
Thanks so much for your help!
Hello!
Thank you for providing this valuable snippet of code!
I was able to add it to my theme files and I have no problem displaying the menu.
I used this variation:
wp_nav_menu( array(
‘theme_location’ => ‘primary’,
‘sub_menu’ => true,
‘direct_parent’ => true,
‘show_parent’ => true
) );
but for some reason, the ENTIRE menu is displayed.
Ex: http://www.rodeo-dev.com/mrc/services-et-responsabilites/amenagement-du-territoire/schema-damenagement/
Obviously I only want the subset to be displayed.
What am I forgetting?
Thanks!
-webfairy
Oh never mind! I have figured it out! 😉
I just needed to change the value for the theme_location parameter!
There might be a plugin interfering with the code, try disabling all your plugins to see if it fixes the issue. Also, check your functions.php file to see if there’s anything else hooking into the wp_nav_menu_objects filter.
If that still doesn’t work, try setting up the code on a completely blank WordPress installation and see if it works there.
Hi Christian, thanks for a super quick response. I had already posted back a couple minutes later to notify you that I had figured it out.
Your code is doing its job marvelously on my site. Thx!
No worries, glad it’s working for you now 🙂
Nice Article. 🙂
This solution is simpler. It’s from http://codex.wordpress.org/Function_Reference/wp_list_pages. The only modification I did is add a “[0]” to
“$children = wp_list_pages(“title_li=&child_of=”.$ancestors.”&echo=0″);”
if(!$post->post_parent){
// will display the subpages of this top level page
$children = wp_list_pages(“title_li=&child_of=”.$post->ID.”&echo=0″);
} else{
if($post->ancestors) {
// now you can get the the top ID of this page
// wp is putting the ids DESC, thats why the top level ID is the last one
$ancestors = get_post_ancestors($post->ID);
$children = wp_list_pages(“title_li=&child_of=”.$ancestors[0].”&echo=0″);
}
}
if ($children) {
echo $children;
}
Mark, if you read my post you’ll see that wp_list_pages doesn’t generate a menu based on the structure you design in the menu builder. I made a very clear point to specify this in the first paragraph. Your solution will not order the menu properly, nor will it even show the right items if you build a custom menu. Please make sure you read & understand an article before commenting on it with an incorrect solution.
post_parent)
$children = wp_list_pages("title_li=&child_of=".$post->post_parent."&echo=0");
else
$children = wp_list_pages("title_li=&child_of=".$post->ID."&echo=0");
if ($children) { ?>
post_parent); echo $parent_title;?>
'cawpc_main_menu',
'sub_menu' => true
) );
?>
post_parent)
$children = wp_list_pages(“title_li=&child_of=”.$post->post_parent.”&echo=0″);
else
$children = wp_list_pages(“title_li=&child_of=”.$post->ID.”&echo=0″);
if ($children) { ?>
post_parent); echo $parent_title;?>
‘cawpc_main_menu’,
‘sub_menu’ => true
) );
?>
Here is the solution if you want to a above this menu the parent page title
post_parent)
$children = wp_list_pages(“title_li=&child_of=”.$post->post_parent.”&echo=0″);
else
$children = wp_list_pages(“title_li=&child_of=”.$post->ID.”&echo=0″);
if ($children) { ?>
post_parent); echo $parent_title;?>
‘cawpc_main_menu’,
‘sub_menu’ => true
) );
?>
Thanks it very easy to use thank you very much
Christian,
Beautiful code, brother, thanks a lot!!!!
Dave
Love the idea of this – it is exactly what I need…but sadly it isn’t working for me. When I use wp_nav_menu() it returns a list of the menu items (all the menu items) as expected, when I do it with the sub_menu => true flag, it also returns all the menu items – except it doesn’t use the menu names from Appearance->Menu, it uses the page titles instead. So I have two problems here, firstly I can’t seem to narrow the list of submenu items to only the submenus of a particular parent, and the fact that it is displaying the page titles rather than the menu item names.
Hi David, it’s unfortunate that you’re having this problem – but as it stands I’ll need a little more information to be able to help you out. The plugin does work (I just tested on 3.7.1), and it uses the title specified in the menu builder. In fact if you take a look at the plugin code, you’ll see it doesn’t do anything to the title; it merely filters the results already provided by wp_nav_menu. This means that something else in your setup is affecting the output. Are you sure the menu you created is set as the Primary Menu? If it’s not the primary menu (and you don’t want it to be), you need to register a custom menu location (in functions.php), apply the location to your menu, and target it using the theme_location parameter in your call to wp_nav_menu. If that doesn’t work, you might have a plugin affecting the output, try disabling all plugins or testing on a clean installation.
Hy Christian , How can i add the parent page tittle to top of this menu? Please Help me
Frank, there are plenty of results for “WordPress get parent page title” in google, so this doesn’t seem like the type of question I need to address. Is there a specific problem you’re having?
No i use your function and i have pages like this
page whit out child ( i dont want page Title)
page whith child ( here i want to display the page Title)
– child page ( here i want to display the parent page Title)
– child page ( here i want to display the parent page Title)
– child page ( here i want to display the parent page Title)
page whit out child ( i dont want page Title)
Sorry if i not was clear But i want to ad the title of the parent pege just where this menu apear
Tanks i solved
post_parent)
$children = wp_list_pages(“title_li=&child_of=”.$post->post_parent.”&echo=0″);
else
$children = wp_list_pages(“title_li=&child_of=”.$post->ID.”&echo=0″);
if ($children) { ?>
post_parent); echo $parent_title;?>
‘cawpc_main_menu’,
‘sub_menu’ => true
) );
?>
Ok, and what have you tried to solve this problem yourself?
This is amazing. Thanks! It’s exactly what I needed for a project I’m working on.
This post was VERY helpful to me. Thanks so much, Christian! I was able to use it to create a ‘walker’ that will display the nth level submenu and its parent menu item, whenever the site visitor is on any of the nth level pages or their parent page. I was really struggling with this till I found your post.
Thanks for your comment Alison, really glad my code helped you out!
Hi. Need a help for creating 2SUB MENUS.
FOR EXAMPLE:
– Home
– About Me
-My Past
-When I Was Born
-My Present
-My Future
Abrar, if your theme doesn’t support a third-level menu, my blog isn’t the place to discuss it. Your question is not related to my blog post, so to keep this thread relevant, please either contact the author of your theme or post a question on StackOverflow.
thank you
Christian,
Thanks guy. I was looking for soething like that. I’m pretty new to WordPress costumization so It has helped me a lot your post. Just one single question. Is any way to retieve the name of the parent menu? I would like to display on the secondary menu with the menu subset a “title” with the parent menu name. I don’t know how to get it. Thanks !
Unfortunately there doesn’t appear to be a function that allows you to get the details of a menu item using it’s ID (not that I can find anyway). So this makes your job harder. Most likely you’ll have to do it manually, which would involve getting the menu items using wp_get_nav_menu_items, then looping through every menu item until it matches the required ID (eg, from $item->menu_item_parent).
Hi,
That is really good example but I’m wondering could that can same solution do with just a simply css.
By default wp_nav_menu is showing all the content with children. There is some classes where you can point if you want to show selected menus + children.
// by default hide all submenus (children)
#sidemenu ul .menu-item-object-page ul,
#sidemenu ul .current-menu-parent ul {
display:none;
}
// show childrens
#sidemenu ul .current-menu-item ul,
#sidemenu ul .current-menu-parent ul {
display:block;
}
ofc if you go thru menus with code it gives some more options to do 🙂
-jani-
I didn’t even think of this, you make a really good point so thanks for sharing! I noticed your CSS doesn’t quite do what my function does; it still shows the name of the current page as the first menu item, and it still shows all the sibling pages of the menu item parent. The latter is easier to fix, you just need to filter by LIs instead of ULs and add a few more selectors. The former is more of a hack by forcing the very first anchor in the first LI to be display: none. Click here to view the changes I made to your CSS to make it work for me. This particular snippet of CSS does solve my problem, but everyone seems to have different requirements so this CSS isn’t really a one-fits-all solution. Usually I’d be all for CSS-only solutions, but in this case I’d prefer to generate the required menu on the server.
Hi,
That is really good example but I’m wondering could that can same solution do with just a simply css.
By default wp_nav_menu is showing all the content with children. There is some classes where you can point if you want to show selected menus + children.
// by default hide all submenus (children)
.menu-item-object-page ul {
display:none;
}
// show childrens
.current-menu-item ul {
display:block;
}
ofc if you go thru menus with code it gives some more options to do 🙂
-jani-
Hi Christian,
This is great, thank you for sharing!
One question:
I am trying to add a class name to the first li item in the list, after the list is sorted
However, putting $sorted_menu_items[1]->classes[] = ‘first’; before the return part adds a new empty li instead of append a class to the first one
I’m trying to figure out where in the code I could append the classname
Any pointers would be much appreciated
Thank you,
Attila
Your code doesn’t work because the sorted array is an associative array; index 0 isn’t necessarily the first item after it’s been sorted (or might not even be in the array depending on the menu structure). When trying to edit data like this, try a print_r() on it first to check the structure of your data first. To get the first item in the array, you can use reset like so: `reset($sorted_menu_items)->classes[] = ‘first’;`. Having said that, a ‘first’ class is very redundant when you have pseudo-selectors in CSS like `:first-child` which are supported in every browser and IE7+.
Thanks!
I tried print_r, but I did’nt figure out it was an associative array. Now it makes sense.
Thanks a lot for your help!
Hi Christian,
First off, what a great solution! I got rid of my old clunky walker menu setup.
My question is, is there any way to have a page show in multiple sub menus? My setup is like this:
Parent Item 1
– Sub Item A
– Sub Item B
– Sub Item C
Parent Item 2
– Sub Item A
– Sub Item D
– Sub Item E
So, with your solution currently, if I click on Sub Item A under Parent Item 1, it shows me the proper sub menu for Parent Item 1. However if I click on Sub Item A under Parent Item 2, it still shows me the sub menu for Parent Item 1. Is there a way to get around this or do I just need to have two different pages?
If you have any questions, please let me know.
Thanks so much,
James
Very interesting find! I’m going to assume that it has something to do with the call to `$menu_item->menu_item_parent`. If the same page is assigned under 2 different menu items, WP probably just picks the first menu item as the parent. I’ll have to check the core to confirm. Perhaps a workaround could be to create a custom menu item (using Links instead of Pages), and link to the page manually for ‘Sub Item A’ under ‘Parent Item 2’. That way you wouldn’t have to create a duplicate page.
Thanks for the reply Christian, I was thinking creating a Custom Menu Item would be a good way to get the menus to display properly, which it does. However, as soon as a user clicks on the custom link in Parent Item 2 (e.g. /parent-item-1/sub-item-a/) they are brought to that page and are shown the Parent Item 1 menu (which makes total sense). This however presents a bit of a usability issue in my case, since I’m essentially trying to create a common page which appears in multiple menus, as it is relevant, but I’m hoping not to have to create multiple pages as that’d be a hassle to update (basically it’s like a Schedule page that has a bunch of content).
As your menu functions currently makes complete sense (and is correct in doing so), I think essentially what I need to find/create is something that says “if on Parent Item 2 and you click Sub Item A, then display the Parent Item 2 menu”.
So I found something interesting, when you try to make a custom link from, for example, Sub Item A under Parent Item 1 to Sub Menu A under Parent Item 2 (which is where the actual page link is) your function still displays the menu for Parent Item 1. It’s not until I specify a different URL other than Sub Item A (e.g. I just tested with ‘#’) that the menu for Parent item 2 is displayed when on the Sub Item A page.
New breakthrough: while specifying the same URL in my custom link URL gave me issues with displaying the menu, if you change it ever so slightly, it will work.
So, this is how my WordPress menu was displayed before:
Parent Item 1
– Sub Menu A custom url= /parent-item-2/sub-menu-a/
Parent Item 2
– Sub Menu A page= /parent-item-2/sub-menu-a/
But when you clicked on Sub Menu A under Parent Item 2, the menu for Parent Item 1 displayed, so I did this:
Parent Item 1
– Sub Menu A custom url= /parent-item-1/sub-menu-a/ (note the change to 1)
Parent Item 2
– Sub Menu A page= /parent-item-2/sub-menu-a/
So while technically the Sub Menu A has a parent of Parent Item 2, specifying the parent-item-1 in the URL for the custom link still resolves to the correct page and forces the menu to display correctly.
Nice find! I’m sure that this is going to help a a lot of people searching for this exact problem – appears you’re the first person to solve it properly!
Hi Christian,
Thanks for sharing this man.
I’ve inserted the code into my functions.php, called wp_nav_menu exactly as shown with ‘sub_menu’ => true, however it’s still creating a standard nav menu with all level one and all level two pages listed, regardless of which page I’m on.
I have registered two other menus (for header and footer) that I’m using. This sub-page menu will go in the sidebar. Do I need to register a new menu for this, or should it work by default?
Cheers
Tristan, it should work by default without you having to create another menu. Have you got your site set up on a staging server where I could have a look to see what’s going on?
Sorry just on a localhost setup. This is the nav tree I’m getting, regardless of the page, or whether I use ‘sub_menu’ => true:
Home
Clients
– Services
– Client Showcase
– Resources
Candidates
– Services
– Candidate Showcase
– Resources
About Us
– Our History
– Our Expertise
– Our Team
– Contact Us
Does the page ‘order’ set in the WP page options make any difference?
I’ve tried removing all other functions (including other menu registrations and calls) and still no luck.
The code has been copied and pasted exactly as above.
Currently running WP 3.6.1
Thanks for any suggestions!
I’ve had a play around with this and I think I might have found the issue. You need to ensure that your custom menu is set as the primary navigation menu. If you log into WP admin, and go to Appearance > Menus, click on the Manage Locations tab, the Navigation Menu should be set to the custom menu you created (and not left blank). Give this a go and see if it works.
Thanks heaps for your time and the extra info man – much appreciated. To clarify my setup, I have two menu areas registered, and I have one custom menu in WP admin that I’ve applied to both of these – called “Top Level Menu”. I’m using this just to display parent pages in the header and footer. Under manage locations I have both locations set to “Top Level Menu”.
I then have the submenu in the sidebar. I’ve just tried setting ‘fallback_cb’ => false to see if the sub_menu option is working (but incorrectly) or if it’s not working and falling back to the default menu – now no menu shows.
By the way, do I need to include the sub pages in my custom menu “Top Level Menu” for this to work, or should it just work by default based on page structure?
Thanks
OK, apologies man I misunderstood the process. I’ve got it working now. I registered a new menu location for the sidebar, and created a new menu with all pages in WP admin and set it to this sidebar location, where the wp_nav_menu with ‘sub_menu’=> true is called. All working nicely!
I got off track because I thought the code would alter the default wp_page_menu structure to only display sub-pages, rather than displaying sub-pages from a custom menu.
Thanks again for all your help!
Yeah it actually pulls the pages from the menu, one of my older functions pulled it from the page structure itself, but this caused issues with custom ordering and the like (aka, it would default to alphabetical ordering) so I rebuilt it to work purely off the structure designed in the menu builder. So yeah, you do need to have a menu with all the pages in it (and nested properly) to work. I was just assuming you had built one menu for to use for everything, which was a bad assumption to make!
Glad you got it working in the end!
how to display only sub menu
Could you elaborate on your problem and desired solution?
Dude, you are the King of the World! Thanks heaps
THANK YOU SO MUCH CHRISTIAN!!! I’m so following your website now >:)
works like a dream !!!! thank you… you saved my life..
Love your work! I was in the middle of fudging around with wp_list_pages when I realised I needed to get sub menu items from the structure of wp_nav_menu and voila! I found this post 🙂
Hi Christian, Thank you for sharing this very useful solution. It works great for tertiary level navigation.
Please could you help me with a small issue.
I constructed my tertiary level menu using WordPress Menu under Appearance.
I display my top level navigation on the top of the page on the header. And I display my secondary and tertiary level menu on my left sidebar which changes based on the top menu selected.
However, the sidebar menu disappears everytime I click a “single post” page from a taxonomy listing page. I need to retain my left sidebar menu even when I’m on a “Single post” page.
Please could you help me solve this issue.
Any help would be much appreciated.
Thank you very much.
Maybe something like that works …
// find the current menu item
foreach ( $sorted_menu_items as $menu_item ) {
if ( $menu_item->current ) {
// set the root id based on whether the current menu item has a parent or not
$root_id = ( $menu_item->menu_item_parent ) ? $menu_item->menu_item_parent : $menu_item->ID;
break;
} elseif ( $menu_item->current_item_parent ) {
$root_id = $menu_item->ID;
break;
} elseif ( $menu_item->current_item_ancestor ) {
$root_id = $menu_item->ID;
break;
}
}
I’m having the same problem with the single news pages (single.php). I can’t seem to get it to display the same menu that is on the main news page. Otherwise this is great!
Never mind…I got it working. This is what I added:
elseif ( is_single() ) {
$root_id = 125;
break;
}
Perfect solution
Thanks a lot for this great solution. I’d like to have a third level navigation based on the children of the 1st level menu items. Is that possible? You mentioned looping with the loop, but 2nd level items then not displaying anymore. Any way to have them shown still?
I’m thinking of creating a separate widget to display the 3rd level menu items in the sidebar when on a 2nd level menu item.
Have you tried using the “direct_parent” => true flag in your call to wp_nav_menu? I’ve got a little description about it at the bottom of the post. If you use this flag the menu will be filtered by the parent one level above the current level. You might want to have 2 menus like you suggested, a sub menu that doesn’t use the direct_parent flag, and a sub-sub menu that does use it.
If that doesn’t work, can you make a dummy menu with some dummy output so I can understand exactly what you’re trying to do?
Thanks for the tip. I will try it as soon as I get back to the code. Will let you know if it doesn’t work. Hopefully it does, though.
Hi Christian,
I’ve tried your solution and applied the “direct_parent” => true flag to the call of my second level menu, and created a widget with a third level menu. Though on the third level menu there’s no luck in showing the third level. It’s only showing the second level menu tree including the third level inside.
I basically want to create something like this: http://d.pr/i/QX7F
Instead it’s showing up like this: http://d.pr/i/x78I
I’ve basically taken your code completely identical with my secondary and third level menu functions looking like this:
function erc_secondary_nav() {
wp_nav_menu(array(
‘theme_location’ => ‘header-menu’,
‘container_class’ => ‘secondary_menu clearfix row’,
‘menu_class’ => ‘clearfix’,
‘fallback_cb’ => ”,
‘container’ => ‘div’,
‘link_before’ => ”,
‘link_after’ => ”,
‘items_wrap’ => ‘%3$s’,
‘sub_menu’ => true,
‘depth’ => 1,
‘direct_parent’ => true
));
}
function erc_thirdlevel_nav() {
wp_nav_menu(array(
‘theme_location’ => ‘header-menu’,
‘container_class’ => ‘thirdlevel_menu clearfix row’,
‘menu_class’ => ‘clearfix’,
‘fallback_cb’ => ”,
‘container’ => ‘div’,
‘link_before’ => ”,
‘link_after’ => ”,
‘items_wrap’ => ‘%3$s’,
‘sub_menu’ => true
));
}
Yeah I can see what you mean now, and my function is specifically designed to display a sub menu based on the current level in view, not a submenu based on the next level down. For your sidebar menu, you’ll need to modify my function to exclude the current level from the tree, so it only shows the third level.
I can see a solution, but i prefer helping people figure it out rather than just posting a working answer. The offending code is on line 15 of my function. You’ll either need to create a new function to display your submenu, or pass it a new flag, because changes you make to this line will affect your second level nav menu.
Give it a shot and see how you go.
I had to abandon this plan since the client went for a different approach. I will try getting this running, though. Since it might still prove useful for future purpose.
Hi Christian,
i´ve got one question. Is it easy to modify the code to get something similar to this?
menu item 1
— submenu 1
— submenu 2
— submenu 3
menu item 2
menu item 3
Would be great if you could help me out.
If you aren’t filtering the menu by a parent, you should just be able to use wp_nav_menu normally without any additional hooks at all.
If that doesn’t work, perhaps you can elaborate on the problem and what you’ve tried to far / explain what you need in a little more detail.
Sorry for my bad explanation. Everything works fine at the moment. But the Problem is that i use it like
true, ‘menu_class’ => ‘nav sub_menu’, ‘container’ => false)); ?>
and so i get
menu item 1
menu item 2
menu item 3
submenu 1 (dependent on menu item1)
submenu 2 (dependent on menu item1)
submenu 3 (dependent on menu item1)
and not the first layout.
Did you try the suggestion in my previous reply? What happens if you don’t pass the sub_menu argument to your call to wp_nav_menu?
I’m still not clear on the problem. Instead of trying to describe it, it’s much easier if you’re able to demonstrate the problem. Can you set up a demo that demonstrates the problem?
Howdy,
I have a menu like the following:
Desitinations
– south africa
– south africa overview
– itinerary options
– another page
– another page
-nambia
– nambia overview
– itinerary options
– another page
– another page
– kenya
– kenya overview
– itinerary options
– another page
– another page
The overview page in each section is actually just the top level repeated. e.g. if you click south africa you are taken to the overview page. All countries are listed down the side of the page, then in a sub menu appearing in another part of the page I woudl like to display the children (overview,itinerary options, other pages)
Using the code above appears perfectly on the itinerary options page, however it doesn’t work on the country landing page. Essentially I want to ensure on the parent page the children pages are shown including the overview link, and on the children pages the siblings are shown with the overview link.
Hopefully that made sense!
Any insight would be greatly appreciated,
thanks in advance,
Rich
Rich, the problem is that your top level country pages are nested under Destinations, so as far as WordPress is concerned Nambia or Kenya aren’t root menu elements. And that’s what my function filters parent/child pages by. I could add a flag to function to solve your problem, but it’s just going to cause feature bloat.
But thanks to WordPress’ menu builder, it isn’t too hard to find a work-around to your problem. You could build another separate menu just for the countries using the countries as root level elements. For example: http://i.imgur.com/eMLG4hF.png. Then, assuming you’re using the nav_menu hook version of my code (the second snippet in my post), you can generate a submenu using the new menu you created in the menu builder.
wp_nav_menu( array(
‘menu’ => ‘destinations’,
‘sub_menu’ => true
) );
I tried it out and it worked for me, but let me know if you have any issues.
Thank you for your reply, Christian.
I see the problem! I have tried to find a work around and am very close! I have tried your suggestion too so I have that as a back up just in case I can’t get the other way going.
Thanks again, mate.
Rich
Thank you Christian Varga by the solution , I solved this issue using jquery because I had the same problem like dlls. I hope to help someone else.
var xdump = $(‘.current-menu-ancestor’).html();
$(‘#tofill’).html(xdump );
Looks like this is a common problem. Personally, I believe it’s more semantic to generate the menu properly in the first place, but for all intents and purposes your code will work for the end user. Don’t forget to wrap it in the jQuery ready function. I might look into modifying my function so it can work either way.
This code is GREAT … but I have one issue.
If my navigation is like this:
About Us
– About Us Subnav 1
–About Us Tertiary 1
–About Us Tertiary 2
–About Us Tertiary 3
-About Us Subnav 2
-About Us Subnav 3
If I am on any of the tertiary pages, I get no context. I can only see the Tertiary pages, but I need to also be able to see the parent page and its siblings (so also see Subnav 1, Subnav 2, Subnav 3).
Thanks!
Yeah that makes sense, I designed it to be able to drill down when sub-menu items are being viewed. What have you tried so far? You’ll probably want to backtrack the root_id to a higher level parent (rather than just the direct parent); there’s a comment below by Thomas Dexter describing one such method, although it relies on the page structure (not the menu structure) which could potentially cause problems.
Such a great filter for wp_nav_menu. However, i need it to work as dills have stated.
Where should i backtrack the root_id and how? Would really appreciate your help!
Have you tried experimenting with the code provided by Thomas Dexter? There’s a link to it in my previous comment, or you can just scroll down to take a look. It’s not perfect because it uses the page’s ancestors (not the menu item ancestors), but for 99% of cases this should be fine. but I haven’t looked into the feasibility of using the menu builder to do this, and unfortunately I’m absolutely swamped and I just don’t have any time to look into developing a solution for it right now.
Ok! It was the code from Thomas Dexter that i use. I really need it to work with menu item ancestors. I think i’m going to use a walker to get the desired effect instead. Thank you anyway!
My code has been updated to address this issue, check my gist for the latest version here: https://gist.github.com/levymetal/5547605#file-functions-php
wordpress 3.5.1 The theme (which is circles by theme smack) is using the wp_nav_menu and only supports one menu. The reason im editing is when i introduce a second level to the menu, the menu option is not styled and stays on the 1st level and flickers in chrome. you can see it here: http://hopskip.us/fairhopesmiles/.
Thanks for responding!
I think you may have misunderstood the purpose of my plugin; it’s only use is to display a secondary menu on a page, containing all children under that page. It is not designed as any kind of replacement for the main navigation.
Your flickering issue has nothing to do with the menu, and all to do with the CSS. The menu is positioned absolutely, and the content is overlapping the menu, and has a higher z-index. Either remove the rev_slider_home_slider shortcode that isn’t working to push the menu back up to it’s correct position and stop the overlap, position the menu relatively, or add the following CSS to ensure the menu always has a higher z-index than the content: .menu-primary-container { z-index: 999 }
will do… thanks guy
do you have any suggested reading on adding a 2nd level menu option to wp_nav_menu on a theme that does not support it?
Are you familiar with using CSS? I haven’t seen any articles on your specific problem, but you just need to edit the CSS, find the styles that correspond to the second level items, and apply the same styles to the third level items, which should be pretty straight forward. I’d recommend heading over to Stack Overflow if you have any problems with that. Either way, you should not need to use my code at all – it’s just a CSS problem.
I am familiar with CSS, and that does help. Thanks for your time.
Great tutorial,
Though when in drop the updated code in my functions and the hook in my header.php file, my menu disappears. Would be open to input from anyone.
How is the menu created in the menu builder? Is it the primary menu, or have you created a secondary menu? What theme are you using, and what’s your version of WordPress?
Dude, thanks for the excellent snippet!
One question though; if the current page doesn’t have any sub-menu items it displays an empty . Is there anyway to test if the current page have any sub-menu items? And if not, do something else.
Which method are you using? I’ve edited my original function with a line that you can uncomment to stop the output of the ul (I also changed the initialisation of the current_menu_id to null instead of 0). If you’re using the nav_menu hook method, there doesn’t appear to be a flag to suppress the menu wrapper if it’s empty, but I found a comment here which helped me write this: https://gist.github.com/levymetal/5547605#file-page2-php. It’s slightly ugly but will get the job done. You might want to move the code into functions.php so you can call it anywhere.
Dude…freaking brilliant. Thank you so much!
God Bless You!!!!
This is great stuff! Thanks a bunch!
Hi Christian – I’ve found a much more elegant solution which still uses the wp_nav_menu so you can use custom walkers and other benefits of nav menu.. It’s work awesome for my subpages to list only their children and grandchildren etc… in their secondary menus. Check it out: http://stackoverflow.com/a/7713875/786094
Using that filter, I’ve also added to my header.php:
if ($post->post_parent) {
$ancestors=get_post_ancestors($post->ID);
$root=count($ancestors)-1;
$topparent = $ancestors[$root];
} else {
$topparent = $post->ID;
}
And then call the menu like so:
wp_nav_menu( array(
‘theme_location’ => ‘primary’,
‘menu_class’ => ‘nav’,
‘depth’ => 3,
‘fallback_cb’ => false,
‘walker’ => new The_Bootstrap_Nav_Walker,
‘start_in’ => $topparent
) );
Thanks for the comment, that’s a pretty nice solution and it works much when the menu has more than 2 levels. However, putting that initialiser code into the header is hardly elegant; you’d be much better off creating a custom function to get your $topparent value, or just putting that code straight into your custom walker (and pass it a flag instead of a start_in value).
Thomas, I took a good look into your code and I liked the concept of using wp_nav_menu a lot more than my original code that was building the menu manually. However, I noticed that your code relies on the page structure to match the menu structure, which isn’t always desirable (using post ancestors etc). Also, if you drill-down into nested pages, your code list all items under the top level parent, instead of the direct parent. This may or may not be desired behaviour, but in my case I wanted the menu constantly filtered as you drill-down into nested pages.
So having said that, I’ve updated my post with a new function that combines your concept, the code from Stack Overflow, and my method of filtering by the menu structure instead of the page structure. I find this to be a much better way of building the custom menu.
Thanks for the code!
Just saved my day! Works great, also the sub-submenu part works just like I need it. Can’t believe this functionality isn’t build in…
Super super super !
This is exactly what I’m looking for however…. What if there is a sub-submenu? This code doesn’t display it. Ideas?
Annoyingly WordPress doesn’t provide a nested array of objects from the wp_get_nav_menu_items function, unless there’s a newer function I’m not aware of (and that’s possible). Anyway, using my code, you need to add another loop inside the loop that prints the list, which loops through every item, checks if it’s parent is the current sub-item, and if so displays it. I wrote a gist here: https://gist.github.com/levymetal/5375456. However, when viewing a sub-sub page (or should we say 3rd level page), it won’t display the second level menu any more. This can be fixed, but I’m not sure how you wanted it to work. If it’s not quite right, give it a crack and see if you can update it to work better for your situation. And if you have any troubles feel free to write back here 🙂
superb tutorial …………