Single custom posts CAN highlight nav (and sub-nav) links, really

Okay, so WordPress’ custom post types are pretty cool. They allow me to eliminate custom fields, a feature that is very useful but all too often confusing to the WP un-washed. Custom posts, combined with custom taxonomies and custom menus, make WP a hell of a CMS contender.

But OOPS, the WP folks took this roast out of the oven before it had reached a proper internal temperature. One very important feature that is missing is the assignment of a “parent page” to all posts of a certain custom post type. Example of a parent page: blog posts all use the site’s blog page (the one that shows the most recent list of posts) as their parent, so whenever you view a single blog post, the parent blog page is highlighted in navigation even if you’re not actually looking at it.

The same is not the case with custom posts. Actually, technically speaking it is the same for custom post types: all custom posts use the main blog page as their parent. That’s not right, not one tiny little bit. And if you’re thinking it’s a simple thing to work around, think again. It’s not a killer, but it’s more convoluted than it should be.

Fear not, I have a solution for you, as long as your site is exactly like mine. Heh.

First an explanation of what this procedure covers:

A two-level page parent system with “immediate” parents (think work category pages on a portfolio site, like Web, Print, Illustration, etc.) and what I can only refer to as a “grandparent” parent that encompasses all “immediate” parents (think general portfolio page, where examples from the Web, Print, Illustration, etc. categories are shown). A custom taxonomy is used to assign a single “immediate” parent category to each custom post.

Now, the procedure in high-level terms:

  1. If Descriptions are not enabled in your custom menu options, enable them (click on the Screen Options tab near the top-right of the Appearance | Menus page and select Descriptions if it is not selected).
  2. In the menu item for the page that you wish to be the “grandparent” parent, enter your custom post name in the Description field. This is the name you passed as the first argument to register_post_type() in functions.php, not the plain English name.
  3. In the menu items for each of the “immediate” parent pages, enter the custom taxonomy term for that page in the Description field. Match the term exactly, right down to capitalization.
  4. Add the following code somewhere in your theme’s functions.php file, changing custom_taxonomy_name to the name you gave to your custom taxonomy:

add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 );
function add_current_nav_class($classes, $item ) {

// Necessary, otherwise we can't get current post ID
global $post;

// Grabs the terms from the current post
$page_tax_terms = wp_get_object_terms($post->ID, 'custom_taxonomy_name');

// Grabs the term object that was returned by wp_get_object_terms()
$page_tax = $page_tax_terms[0];

// Grabs the post type of the current post
$page_post_type = get_post_type();

// Grabs the Description of the current menu item, trims whitespace
$item_desc = trim($item->description);

// Do the magic...
if ($item_desc != '' && (($item_desc == $page_post_type) || ($item_desc == $page_tax->name))) {
       $classes[] = 'current-menu-item';
};

// Return the corrected set of classes to be added to the menu item
return $classes;

}

 

That should do it. If you did everything listed above, hopefully your “immediate” and “grandparent” parent pages will have a current-menu-item CSS class assigned to their LI in HTML. You can take the CSS highlighting from there.

All you folks that got it? Feel free to stop reading here. And congratulations.

Folks that got lost, here’s a detailed walk-through that explains how I did this on my website:

My scenario, explained:

  • I have three main pages on the site: Home, Blog, and Portfolio.
  • I have a custom post type called mpdx_piece that I use for my portfolio pieces.
  • I have a custom taxonomy called mp_pieces that holds my categories of work: Web, Print, Logos, Illustration, Icons, and Etc. Each portfolio piece is assigned exactly ONE category.
  • Each of these categories of work has a page on the site that exclusively shows portfolio pieces of that category. So, for example, the Web page only shows portfolio pieces assigned to the Web category. I made the page name exactly match the work category for simplicity.
  • I have a custom menu that starts with the three main pages, then the work category pages are positioned under the Portfolio page (they’re “children” of the Portfolio page). Here’s a borderline worthless image of what the menu looks like in Appearance | Menus:

Image of my custom menu, with indentation to show parent/child page relationship.

 

As I mentioned earlier, Descriptions need to be enabled in the menu item options. If they aren’t, instructions for enabling them are above.

Descriptions as well as other customization options are accessed by clicking on the down-arrow icon on the right end of the menu item gray bar. The form that pops up should look like this (with different form values, of course):

 

Form that holds custom menu item options. Access by pulling down the arrow icon on the end of the menu item gray bar.

Form that holds custom menu item options.

 

The unique Descriptions in each menu item will be compared to the output of one of two WP/PHP functions that are called when pages are loaded. We have two menu items to highlight for each portfolio piece, so two different comparisons will be performed: one to highlight the Portfolio link (for the portfolio piece’s “grandparent” parent page), and a second to highlight the work category page link (for the portfolio piece’s “immediate” parent page).

 

Example of the site's portfolio menu showing both parents that require highlighting.

Example of the site's portfolio menu showing both parents that require highlighting.

 

The Portfolio menu item needs to be highlighted for all portfolio pieces regardless of what work category was assigned to them, so something that WP/PHP can detect on all portfolio pages–something with a value that does not change from page to page–needs to be in the Portfolio page menu item description. The only thing that fits the bill is the name of the custom post type. It never changes from portfolio piece to portfolio piece, so the output of get_post_type() (we’ll get to that in a bit) will always be the same regardless of the piece being viewed. For this reason, mpdx_piece (my custom post type name) is entered as Portfolio‘s Description.

For the work category menu item Descriptions, enter the name of the work category exactly as it appears in the work category custom taxonomy, capitalization and everything. So, for example, in my Web menu item, the Description is Web.

That covers the menu item options. A “hook” in functions.php will be used to add a CSS class to the menu items that require highlighting. The hook is called nav_menu_css_classes, and the CSS class being added to the parent menu items is called current-menu-item. The hook is run every time a page loads, so for each (re)load, the Descriptions in menu items will all be checked against information associated with the current portfolio piece page, and when a match is found, the menu item will receive the CSS class.

First, the code, added some where in the theme’s functions.php file:

 

add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 );
function add_current_nav_class($classes, $item ) {

// Necessary, otherwise we can't get current post ID
global $post;

// Grabs the terms from the current post
$page_tax_terms = wp_get_object_terms($post->ID, 'mp_pieces');

// Grabs the term object that was returned by wp_get_object_terms()
$page_tax = $page_tax_terms[0];

// Grabs the post type of the current post
$page_post_type = get_post_type();

// Grabs the Description of the current menu item, trims whitespace
$item_desc = trim($item->description);

// Do the magic...
if ($item_desc != '' && (($item_desc == $page_post_type) || ($item_desc == $page_tax->name))) {
       $classes[] = 'current-menu-item';
};

// Return the corrected set of classes to be added to the menu item
return $classes;

}

 

I highlighted mp_pieces in the code above. That is where the custom taxonomy for work categories is indicated. This is where the hook gets the category that was assigned to the portfolio piece, which is then compared to the Description of each custom menu item.

Really, that should be it. I don’t cover the CSS because each site is different, so my CSS instructions would break on some systems. I figure you know CSS already anyway. If not, sorry.

Is this the most robust solution? No, not by a long shot. I could add code to make the taxonomy categories case-insensitive. I could add descriptive errors. I could do lots of things, but really, the basics are all that are necessary to get up and running. Hopefully I covered them all well enough. Probably not. Let me know.

And hopefully these instructions will become obsolete very, very soon. Seriously, WP, fix this.

This entry was posted in css, customposts, customtaxonomy, themes, webdev, WordPress.

3 Responses to Single custom posts CAN highlight nav (and sub-nav) links, really

  1. Eb says:

    Love this!! Thanks =)

  2. Hi Erik, you mentioned the exact problem I have been having in your second paragraph above but then digressed off onto a related problem and solved that marvelously. There are so many answers out there describing how to get single wordpress posts to highlight catagories but none suggest a solution to the problem I am having!! You have come the closest by at least mentioning it above. I will explain…

    The problem I am having is that I have a menu bar across the top of my wordpress generated web site. All of the pages on the web site are static pages except for my blog page. This blog page shows the summary of my most recent blog posts. You then click on one of them to read the single post. How for the love of all that is holy (I have been searching for an answer to this for just over four hours now and if I read one more answer about catagories I am going to throtle something!) do you get the “Blog” menu button to stay highlighted as the active page when you are reading a single post. I understand that single posts are not recognised by WordPress as ancestors of the Blog page that they link from but there has got to be a way of getting those single posts to cause the Blog menu button to become highlighted.

    Any suggestion you might have would be enormously appreciated!!

    Many thanks,
    Haydn

  3. erat says:

    Howdy Haydn,

    The only thing I can think of is this: in CSS, you’re not highlighting the correct class for the blog menu item. I can’t remember if a different class is added to the blog menu item when viewing single posts but it’s worth checking, and if so, be sure to style the class accordingly.

    I didn’t do anything special to highlight “blog” in my menu outside of CSS styling, or at least I don’t recall doing anything special, so that’s my best guess.

    Yeesh, I hope this helps!

    E.

Leave a Reply

Your email address will not be published. Required fields are marked *

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