Dynamic category menu highlighting for single posts

Cyberspace is a place
Gosh! That’s a mouthful, isn’t it, but what does it mean? Quite simple, really. In this tutorial we’re talking about how to dynamically highlight the relevant category menu item when viewing a single post which belongs to that category. Look at my menu bars and you should see that Code Snippets is highlighted – which is pretty cool given that you are browsing a single post.

The following assumes that (a) your theme has a category navigation menu bar or similar, and (b) this menu is generated by the WordPress wp_list_categories() function.

Before we begin I have to confess that I didn’t come up with this code all by myself. The code that we will use is derived from this www.designshard.com article, but has been tweaked by me to make it even more useful for day-to-day use. The problem with the method outlined in that article, and the Show Category Archive plugin that is referenced therein, is that those methods add a CSS class to the menu <a> tag, whereas the commonly used menu function wp_list_categories adds dynamic “menu state” classes to the links’ li tag. It seems much more useful to try to fit in with what WordPress is already doing, rather than create something outside that structure.

Anatomy of a wp_list_categories menu

By all means, be my guest and dig into the wp_list_categories source code to see exactly how wp_list_categories works. But let’s not get too technical, and look at a simple example instead. Here’s the example menu code from a typical theme’s header.php template file:

<div id="subnavbar">
	<ul id="subnav">
		<?php wp_list_categories('orderby=name&title_li=&depth=4'); ?>
	</ul>
</div>

If we view the Page Source of a page other than a Category archive page, this is the markup that this code generates:

<div id="subnavbar">
	<ul id="subnav">
		<li class="cat-item cat-item-1"><a href="url to fruit category page" title="View all posts filed under Fruit">Fruit</a></li>
		<li class="cat-item cat-item-2"><a href="url to veg category page" title="veg category desc">Vegetables</a></li>
		<li class="cat-item cat-item-3"><a href="url to dogs category page" title="dogs category desc">Dogs</a></li>
		<li class="cat-item cat-item-4"><a href="url to penguin category page" title="penguin category desc">Penguins</a></li>
	</ul>
</div>

Putting aside the diverse range of topics handled by this category menu, you will notice that the function automatically outputs 4 pieces of code for each category:

  • cat-item – a CSS class added to the li tag of every category in the menu.
  • cat-item-x – a CSS class added to each li tag, where X is the category ID number. Very useful if you want to style individual menu items differently.
  • A link to the relevant category archive page (not shown in the above example, in order to keep things simple).
  • A title attribute for each link. Notice that this is pulled from the Category Description, assuming one exists, and if it doesn’t, automatically adds “View all posts filed under (category name)” to the link’s title attribute.

If we were to click on the Fruit category link and look at the Page Source of this page, this is what we will see (simplified for clarity):

<div id="subnavbar">
	<ul id="subnav">
		<li class="cat-item cat-item-1 current-cat">...link stuff...</li>
		<li class="cat-item cat-item-2">...link stuff...</li>
		<li class="cat-item cat-item-3">...link stuff...</li>
		<li class="cat-item cat-item-4">...link stuff...</li>
	</ul>
</div>

As you can see, wp_list_categories has added a CSS class of current-cat to the li tag of the Fruit category. By adding a suitable CSS style to the stylesheet to target current-cat, it’s easy to highlight the menu item when viewing any category archive page.

However, as soon as you click on a Post title, this highlighting is lost. This is because wp_list_categories relies on the category query variable, sent to the server when a category link is clicked, in order to know which category li tag to add the current-cat to. When viewing a single post, this query variable is not available to wp_list_categories, therefore the current-cat disappears from the generated markup.

The challenge

In order to get around this problem, we need to find a way to preserve the current-cat class in the relevant category. Happily, thanks to WordPress filters, this is easy to do (yes, there is a wp_list_categories filter), so all we need do now is come up with a suitable function for this filter, which needs to do the following:

  • Find out which post is being requested
  • Determine which categories are assigned to this post
  • Add current-cat to the li tags of these categories (as posts could be assigned to more than one category)
  • Return the result of the above back to WordPress so that it can continue generating the page to be viewed.

The function

Here it is:

function sgr_show_current_cat_on_single($output) {

	global $post;

	if( is_single() ) {

		$categories = wp_get_post_categories($post->ID);

		foreach( $categories as $catid ) {
			$cat = get_category($catid);
			// Find cat-item-ID in the string
			if(preg_match('#cat-item-' . $cat->cat_ID . '#', $output)) {
				$output = str_replace('cat-item-'.$cat->cat_ID, 'cat-item-'.$cat->cat_ID . ' current-cat', $output);
			}
		}

	}
	return $output;
}

Let’s take a closer look…

  • Line 1: Remember – Filters are a way of adding additional processing into the normal data processing flow of WordPress. Therefore, there will be something coming in to our filter function (the $output argument), which is then processed by our function, and then is returned back to WordPress when we’re finished (see line 18). In this case, $output will be the normal output of wp_list_categories being, essentially, the markup shown in the first block of code above.
  • Line 3: We need access to the $post variable so that the function knows which post is being called.
  • Line 5: This check using Conditional Tags ensures that our function only does something when a single post is being called. That’s something to remember with Filters – only process what actually needs to be processed. On every other type of page, such as when generating a category archive page, we don’t want our filter function to affect the normal output of wp_list_categories, hence this check.
  • Line 7: Now that we have access to the $post variable, we can use the wp_get_post_categories() function to generate an object containing database details of all categories assigned to the post.
  • Line 9: We now use a foreach loop to loop through each category found.
  • Line 10: Using the get_category() function we get access to the data specific to the category being looped through. Specifically, we want to get hold of the category ID number.
  • Line 12: We now use a PHP function preg_match() to search for “cat-item-1” (for example) within the li tags contained in $output (the markup generated by wp_list_categories and which we’re processing).
  • Line 13: If line 12 finds a match, we then use another PHP function, str_replace(), to replace “cat-item-1” with “cat-item 1 current-cat”. Hey Presto! We’ve now done the important bit – added current-cat to the li tags belonging to the categories the post has been assigned to.
  • Line 18: Finally, we pass our modified $output back to the wp_list_categories function, for final processing by WordPress and the rendering of the page markup.

Add this function to your theme’s functions.php file.

Adding the Filter

Now that we have our function, we need to tell WordPress to use it by adding the necessary Filter, again in functions.php:

add_filter('wp_list_categories', 'sgr_show_current_cat_on_single');

Adding CSS styles

The final step is to add a suitable CSS style to the theme’s style.css. Each theme will be different, so you may need to experiment with different selectors in order to find the right one.

Taking our example markup as a starting point, something like this should work:

#subnav li.current-cat a { ...put your styles here...}

As mentioned, depending on the structure of your theme’s markup, you may need to experiment a little to get the selector right. The Firebug add-on for Firefox is an excellent tool to help you do this.

Comments

  1. Great tutorial!

    Do you know if this will work for pages, as well? I’m currently building my WP site using page templates, rather than category templates for displaying category posts (I thought it would create cleaner web-friendly URLs). Do you know if I could simply exchange ‘categories’ with ‘pages?’

    Thanks

    • Glad it was useful. πŸ™‚

      Yes, in principle, though the markup output by wp_list_pages() is different to that described for categories.

      If I have time, I may do a follow up article using pages.

      • Ade,

        Sorry to populate your comments with so many questions, but you really seem to know your stuff.

        I’ve abandoned using wp_list_categories for navigation and I will now be using wp_list_pages (for numerous reasons). I would really like to dynamically highlight my page menu when on single posts. I’ve found that many other people are also looking for a solution. Any chance you could do a follow up article on this . . . soon? πŸ˜‰

        Thanks,
        Brent

  2. Another question for you . . .

    Do you know if it is better to use Pages Templates or Category Templates to display info for search engines? The category template adds “/category/” to the URL before a category title. I would love to hear your thoughts πŸ˜‰

    • Frankly, I don’t know. I’m sure there are a million sites out there which will express a variety of views. I like to think that, primarily, Search engines want human users to be able to get relevant search results in order to find what they’re looking for.

      I guess the answer depends on the content of the site – is navigation and usability more logical and more easily obtained with posts/categories rather than pages? The answer will vary from site to site.

      • The site I’m building is a creative services directory which uses many levels of categories to help narrow searches. I’ve done a bit of research on this page template vs. category template question, but haven’t found many answers. Maybe this would be a good article/post topic?

        Thanks for your insight!

        • My view is that if you have regularly changing content, combined with a need for a taxonomy whose functionality can be provided by Categories, go for it. I’m a firm believer in “content is king”, and that structured content, good keyword density, etc is the best way to good SE rankings. Pages have certain limitations (though this may improve in future versions of WP) and are not, in my opinion, suited to constantly added content and complicated hierarchies.

          Agreed – could be an interesting topic for a future post. πŸ™‚

  3. Ade,

    This solution works beautifully! Thanks!

    On a side note: do you have any idea how to achieve dynamic category menu highlighting for 3rd level(or grandchild) categories? WordPress has yet to develop a “.current-cat-ancestor” css class for categories (like they have for pages: “.current_page_ancestor”).

    I’ve been pulling my hair out trying to find a solution.

    • Brent,

      It does work – but there are a couple of points to remember.

      1. If you’re using dropdowns, the grandchild category name will only be visible on mouseover, hence you won’t see any highlighting when the menu is closed.

      2. If you want the highlighting to appear when the dropdown is open (if you see what I mean), you’ll need to add suitable styles in the CSS.

      For example, let’s say you have a style for highlighting your top level categories, like this:

      #subnav li.current-cat a {
      	color: #000000;
      	background: url(images/excerpt_flash_small.png) no-repeat 0 0;
      	}

      You would also need to target the next level of li tags, like this:

      #subnav li li.current-cat a {
      	color: #000000;
      	background: url(images/excerpt_flash_small.png) no-repeat 0 0;
      	}

      Assuming you’re using pseudo-classes, eg a:hover, a:link, a:visited, a:active, you will have to modify the above to target these too.

      Another point to consider is whether you assign a post to all categories in the hierachy, eg to the parent, child and grandchild cats. If you do, the highlighting will appear on all three cat tabs – which is probably confusing to the site visitor. Only ever assigning posts to one category (in any one hierarchy) is probably a better solution IMO.

      • Ade,

        Thank you for your detailed reply. I still can’t get my parent link to stay highlighted once clicking down to the 3rd child link. I tried adding a:active, li li, etc., but it just isn’t working. May be it isn’t working because the links are in two separate navigation menus (main nav and sidebar)?

        Anyway. Thanks for trying to help out on this.

        Cheers,
        Brent

        • Hi Brent,

          Yes, I assumed you were talking about only one instance of wp_list_categories(), ie one menu bar. You would need to explain how your two menu bars work.

  4. Hello!

    I would like to thank you for useful post πŸ™‚

    I’m wondering if there is a way to highlight parent category if there subcategories too, but when i select just subcategory as category base for post..

    Hope you understand me πŸ˜€

  5. Really clear explanations indeed, makin’ the things much easier as compared to a whole bunch of infos displayed here or there…
    Currently working on a site under development and worked liked a charm.
    Warmful thanks & Gruezi,
    Frederic

  6. So is there any chance to highlight β€œempty” parent category with a post assigned to a child category?

    Thanks,
    RAzz

    • Honestly, I haven’t tried it. Bit tied up at the moment but I will revisit this at some point.

      • Thanks for answer Ade.. If I find solution for this, i ll post it here..

        Regards

        • Also tried to do this with no success so far, as I don’t want to have my homepage invaded by a dropdown menu. Made a couple of tests from Ade’s code trying the sub-cat tag but it doesn’t look like it’s accepted.
          The only thing I could think of so far is using some conditional statement like is is cat something or somewhat, although I have not tested this yet.
          Peace everybody,
          Frederic

        • The method I used in this article won’t work with an “empty” category. This is because the empty cat ID will not be found by this:

          $categories = wp_get_post_categories($post->ID);

          There may be another way to do by detecting category parents (as hinted at by Frederic in his comment), but I haven’t tried it personally.

        • I believe I may have come up with a solution to this. As with others, I have parent (empty) categories that needed highlighting too. I’ve still got some testing to do on it, and ill be doing an article on my website (crediting this article as its base), if it is successful, I’ll provide the link once I do.

  7. Well the last comment didn’t seem to submit correctly (though I apologize in advance if it did). Anyway, all my tests seem to have worked, and I’m looking forward to seeing if it does for everyone else.

    The Article can be found here

  8. Awesome function! Thanks for the code snippet πŸ™‚

  9. Nice solution. Using regular expression on returned value of wp_list_categories() is cool. Thank you for sharing this post.

  10. Karl Bedingfield says:

    @ Alan Holmes

    Alan,

    I see your site has been hacked but I managed to find a cache of your amended code.

    My question: I see you have a revised version dated March 2010 which works just fine but is there a way to have parent category highlighted when you are on a category page for a grandchild (parent > child > grandchild)?

    Everything works just fine apart from 3 level category.

    Thanks,
    Karl

  11. Hi there,

    this is a related, but hopefully simpler question.

    I’m trying to generate a standard list of my categories on a page – just using the vanilla: wp_list_categories

    Currently, I’ve got the categories and my archives listed in the sidebar, a standard theme.

    -I’ve moved both of them to their own pages
    -I’ve installed a plug in to allow php to be executed in articles
    -The list archives function works in a page
    -The list categories doesnt

    – both lists are still being generated in the sidebar
    – I’m on 2.9.2 I believe, the plugin is Exec-PHP, and the other plugins active are Feed Locations and WP-Spam Free.
    – I’ve disabled the none-html view for the editor….

    So, does anyone know why the archive call works but the categories call doesn’t?

    Many thanks, Gavin

  12. Great tutorial, thanks for writing this up. Any suggestions for version 3.0.1 with the dynamic menus? Searched the forums on wordpress.org; surprised that no moderator has answered this question, yet it has been asked several times on there.

  13. Here’s some help for getting parent category menu items to stay highlighted when you go to a child category.

    The wp-includes/nav-menu-template.php file lists the functions and classes used for the new Custom menu system in WP 3.0

    Classes to use:

    .current-menu-ancestor
    .current-menu-parent

    You can style them to fit with your theme’s css.

    .menu li.current-menu-ancestor a,
    .menu li.current-menu-parent a
    { color: whatever; background: whatever }

    It works with superfish dropdown menus too.

    • Yep, can confirm this.

      Currently there are ( “current background color” and “current parent background color” ) that works with Thesis 1.8 menu and WP-menu(3.x) for the top level Tabs.

      For my test site ( http://pierrecote.fondationcem.org/ ) I have achived that using the ” WordPress nav menu ” and 6 lines of CSS.

      First (1) level highlighting ( white ). Second (2) level highlighting ( yellow ). Third (3) level highlighting ( orange ) – ( without plugins / PHP / jQuery ) –

      1 – Highlighting the ( top categories Tabs ) while on a “single post” of my top categories.

      .current-post-ancestor a {background: #FFFFFF;border-bottom-color:#FFFFFF;}

      2a – Highlighting the ( subcategories ) while on a “single post” of my child/subcategories.

      .sub-menu .current-post-ancestor a {background:#F0EEC2;}

      2b – Highlighting the ( parents and child/subcategories ) while on a child/subcategories. ( need to define a category for the appropriate posts )

      .cat_galerie-2010 .current-menu-parent a {background:#F0EEC2;}
      .cat_galerie_2011 .current-menu-parent a {background:#F0EEC2;}

      3 – Highlighting the ( parents and child/subcategories ) while on a “single post” of my child/subcategories. ( need to define a “CSS Class” (post edit) for the appropriate posts ) – here I target a “CSS Class” and it’s appropriate menu-item created by WordPress )

      .postgalerie2010 .menu-item-776 a {background:#FFD596;}
      .postgalerie2011 .menu-item-785 a {background:#FFD596;}

      BTW, this does not play well with IE.

      —- I have my sub-categories highlighted in my sidebar by :

      As I new that there are no pseudo-selectors for “current state” for these links.

      I use my cat Class + created a Class for my links. I’m using a text widget in my sidebar and added this to it :

      ( Galerie 2010
      Galerie 2011 )

      And in custom CSS :

      ( category identity + link Class )

      .cat_galerie_2011 .gal11 {color:red;}
      .cat_galerie-2010 .gal10 {color:red;}

      For “single post” of these categories. ( posts “CSS Class” + link Class )

      .postgalerie2010 .gal10 {color:red;}
      .postgalerie2011 .gal11 {color:red;}

      Pierre

  14. Hi,
    I’m not sure if this can help anyone, but I was looking for a way to highlight the parent category and with this wasn’t working for me (it was adding the class to the current category only).
    I’ve found this plugin http://kahi.cz/wordpress/highlight-used-categories-plugin/ which is actually a piece of code that you can copy and paste in your functions.php. It works great for me.

  15. I can’t get this to work with a custom content type and a custom taxonomy. Any ideas?

    • You will need to replace the wp_get_post_categories() function with something like wp_get_object_terms(), eg like this:

      $taxonomy = 'whatever your tax registered name is';
      $categories = wp_get_object_terms( $post->ID, $taxonomy );
      • Amazing. It works very well with my custom taxonomies! The name of my taxonomy is directors_name so this is what I put in my functions.php

        function sgr_show_current_cat_on_single($output) {
        global $post;
        if( is_single() ) {
        $taxonomy = 'directors_name';
        $categories = wp_get_object_terms( $post->ID, $taxonomy );

        foreach( $categories as $catid ) {
        $cat = get_category($catid);
        // Find cat-item-ID in the string
        if(preg_match('#cat-item-' . $cat->cat_ID . '#', $output)) {
        $output = str_replace('cat-item-'.$cat->cat_ID, 'cat-item-'.$cat->cat_ID . ' current-cat', $output);
        }
        }
        }
        return $output;
        }

        add_filter('wp_list_categories', 'sgr_show_current_cat_on_single');

        And this is what I put in my single.php

        Thank you for your help!
        / A

        • Hi there,

          I am wondering if there is a way to also add a class for current-cat-parent when inside a child post?

          Thanks,

  16. Angelo says:

    Hi friends,
    I followed this guide and it works great, but the single post in children categories don’t active the main items menu.

    Do you have any solutions?

    • Angelo,

      I think this is discussed elsewhere in the comments.

      • Angelo says:

        Yes….can you see the solution?…If there is I not understand it. Can you explain me, please?

        I need the php code to assign current class to main categories.

        Thanks

  17. Daniel says:

    Fantastic post, and great comments too – reading it pointed me in the direction of using wp_nav_menu instead of wp_list_pages or wp_list_categories

    It makes everything so easy – adding classes for the first and last list item, setting up the order of list items and also styling the parent menu item if the current page is a sub page, as discussed above

    If you haven’t already, do check out Justin Tadlock’s excellent walk-through of wp_nav_menu here –

    http://justintadlock.com/archives/2010/06/01/goodbye-headaches-hello-menus

    • Daniel,

      You are quite right – the new Custom Menu functionality makes all of this so much easier. Generally, I always recommend clients to use this rather than the “traditional” method of wp_list_categories etc.

      Glad you found the article useful.

  18. Thank you very much. It works ad hoc.

  19. Did a similar stuff with the WP Walker class:
    https://github.com/hkirsman/category_aware_walker_nav_menu

  20. Marcelo says:

    Hi.

    IΒ΄m using the plugin here mentioned ‘http://kahi.cz/wordpress/highlight-used-categories-plugin/’.

    My main categories are disposed by ‘male’, ‘female’, and ‘children’.
    The subcategories are the same for each of the 3 main categories.

    So, the problem I found with the plugin, is that it adds ‘used-cat’ on every ‘acessories’ subcategory it finds.
    Regardless of the main category in use.

    It adds ‘used-cat’ on ‘acessories’ of ‘female’ main category, and it adds ‘used-cat’ on ‘acessories’ of ‘children’ main category.

    I would like the code of the plugin to add ‘used-cat’, only to the sub-category of the main category in use, in the current post.

    You can check it out here.

    The website is in portuguese so ‘male’ means “Masculino”, “female” means “Feminino”, and “Acessories” means “AcessΓ³rios”.

    Any idea what modification in the plugin code should I make?

Trackbacks

  1. wp_list_categories() current_cat on single post page - WordPress Tavern Forum

Leave a Reply to Razz Cancel reply

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

*


3 × seven =