(Moderators note: Was originally titled “wp_nav_menu Ancestor class without children in navigation structure”)
I have a wp_nav_menu
in my header which had three pages in it. When I am on one of those pages, the li
containing that page in the menu gets the class .current_page_item
. These three pages have templates, and these templates contain custom queries to get all posts of a certain content type. In effect, the perceived “children” of this top level page are not actually children, they’re just of a content type I’ve associated with that top level page using a template.
I’d like the top level menu items to get a 'current-ancestor'
class when the user is browsing a single page of a specific post type, again, associated with that page only in a custom query in the template file.
Hope that makes sense – if not, let me know where I lost you! Very much appreciate any help.
–Edited for specifics:
For example, I have a static page called Workshops that is using a template. Its slug is workshops. The template has a custom get_posts function and loop within it, which pulls and displays all posts of a custom content type called workshops. If I click on one of these workshops’ title, I’m taken to the full content of that piece of content. The permalink structure of the custom post type is set to workshops/postname, so as the user sees it, these pieces of content are children of the Workshops page, when in reality they’re all of one content type but unrelated to the page. It’s that gap that I need to effectively close in the menu, highlighting the ‘Workshops’ menu item when browsing content of type ‘workshop’.
Again, hope that makes sense, I think I said ‘workshop’ upwards of 20 times in one paragraph!
There’s a simpler solution. Forget creating pages for each post type just so you can have nav items, because as you have learned, WP has no way of recognizing that the custom types you are browsing are related to that page.
Instead, create a custom link in Appearance->Menus. Just put the URL that will return your custom type and give it a label, then press “Add to Menu”.
http://example.com/workshops/
or non-pretty-permalinks:
http://example.com/?post_type=workshops
this alone will simply create a nav button which displays all posts with that custom post type, and will also add the current-menu-item class when you’ve clicked that nav item – but it won’t yet add the nav class on any URL other than this one
Then, once it’s created, go into the configuration for that new item, and enter the slug of the custom post type in the “Title Attribute” field (you could also use the description field, but that one is hidden in the admin screen options by default).
Now, you need to hook the nav_menu_css_class
filter (which gets fired for each nav item) and check if the content being viewed is of the post type indicated in your custom nav item:
add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2 );
function current_type_nav_class($classes, $item) {
$post_type = get_query_var('post_type');
if ($item->attr_title != '' && $item->attr_title == $post_type) {
array_push($classes, 'current-menu-item');
};
return $classes;
}
In this case, we’re going to check that the Title Attribute field contents aren’t empty and if they match the current post_type being queried. If so, we add the current-menu-item class to its class array, then return the modified array.
You could modify this to simply match the title of the nav item, but if for some reason you want to title the nav item differently than the plain slug of the post type, using the Title Attribute or Description field gives you that flexibility.
Now any time you’re viewing a single item (or probably even archive listings) of a post type that matches a nav menu item, that item will be given the CSS class current-menu-item so your highlighting will work.
No pages or page templates needed 😉 The URL query takes care of fetching the right posts. Your loop template takes care of displaying query output. This function takes care of recognizing what’s being shown and adding the CSS class.
BONUS
You can even automate the process using wp_update_nav_menu_item
, by having menu items automatically generated for all your post types. For this example, you would need first to have retrieved the $menu_id
of the nav menu you want this items added to.
$types = get_post_types( array( 'exclude_from_search' => false, '_builtin' => false ), 'objects' );
foreach ($types as $type) {
wp_update_nav_menu_item( $menu_id, 0, array(
'menu-item-type' => 'custom',
'menu-item-title' => $type->labels->name,
'menu-item-url' => get_bloginfo('url') . '/?post_type=" . $type->rewrite["slug'],
'menu-item-attr-title' => $type->rewrite['slug'],
'menu-item-status' => 'publish'
)
);
}