WordPress adds a class of .current-menu-item
to menu items when you are on that page. But sometimes you might want a menu item marked active in other instances.
On my website, I wanted the “Blog” menu item to be marked active any time you’re in the blog (blog homepage, category archive, single post…). I wanted “Code” marked active any time you’re in the code section (Code Snippets listing, Code Tag, Single Code Snippet). And I wanted “Case Studies” marked active when viewing the case study section (Case Study Listing and Single Case Study).
Luckily WordPress provides a filter for that: nav_menu_css_class
The filter includes four parameters:
$classes
(array) – the current CSS classes on the menu item$item
(object) – the current menu item object$args
(array) – the arguments for the current menu (what was passed towp_nav_menu()
)$depth
(integer) – the menu item’s depth in the menu
For this we will only use the first three parameters. I only want my customizations to apply to my menu in the ‘header’ theme location, so I check that first. Then I see if we’re on a page I want marked active (ex: is_singular( 'post' )
) and if I have the right menu item selected (ex: 'Blog' == $item->title
). If all of those conditions are met, I add a class of .current-menu-item
to the menu item.
<?php | |
/* | |
* Customize Menu Item Classes | |
* @author Bill Erickson | |
* @link http://www.billerickson.net/customize-which-menu-item-is-marked-active/ | |
* | |
* @param array $classes, current menu classes | |
* @param object $item, current menu item | |
* @param object $args, menu arguments | |
* @return array $classes | |
*/ | |
function be_menu_item_classes( $classes, $item, $args ) { | |
if( 'header' !== $args->theme_location ) | |
return $classes; | |
if( ( is_singular( 'post' ) || is_category() || is_tag() ) && 'Blog' == $item->title ) | |
$classes[] = 'current-menu-item'; | |
if( ( is_singular( 'code' ) || is_tax( 'code-tag' ) ) && 'Code' == $item->title ) | |
$classes[] = 'current-menu-item'; | |
if( is_singular( 'projects' ) && 'Case Studies' == $item->title ) | |
$classes[] = 'current-menu-item'; | |
return array_unique( $classes ); | |
} | |
add_filter( 'nav_menu_css_class', 'be_menu_item_classes', 10, 3 ); |
I’ve placed this code in my theme’s functions.php file. Normally I recommend using a Core Functionality plugin for code snippets, but this one relates directly to the theme. On my next theme I might not have a ‘header’ theme location for a menu, or I might not want these menu items marked active.
Dave says
Bill,
Thanks, that’s a nice organizational idea, and very cleanly done.
Dave
Sharon says
Hello Bill!
Thanks so much for this great customization tip!
Would this also apply to pages? For instance, I would like to have 3 sub-pages of a main menu item for the mobile version of my website (in-the-works). I do not want these sub-pages included in the main navigation. Instead, I would like the primary menu page to be highlighted, when a visitor lands on each of these 3 sub-pages.
Would your snippet work for something like this? Do you have any clues as to how I could apply it to a custom mobile menu?
Thanks again!
Sharon
Bill Erickson says
You could use one of the existing CSS classes (current-menu-ancestor, current_page_parent… something like that, don’t remember offhand). Or you could use this approach: https://gist.github.com/billerickson/5a388ec744eef44daccb
Sharon says
Thanks SO much, Bill!
hafiz says
Thanks Bill for this very effective customization tips.keep it up.
Maria Campbell says
Thanks so much Bill for all your posts. They are so awesome, and I learn a lot! It makes me love Genesis even more.
David says
Hi Bill,
thank you. i just use it for a site. very smart. i previously manually add custom class in menu item and style it conditionally in CSS (compared to body class).
just a small improvement:
when we set
current-menu-class
in a complex conditional, sometimes we can get double class(es). we can remove duplicate classes using array_unique:return array_unique( $classes );
of course, duplicate css class still works, but it is much cleaner without duplicate 🙂
i also use
array_unique
when filtering body class, post class, etc.thanks again for the tips.
Bill Erickson says
Good call! While not necessary on body_class and post_class since they already run through array_unique after the filter (here and here), the nav menu css classes don’t go through array_unique (here).
I’ve updated my code above to reflect this suggestion.
David says
I didn’t know that wp updated body class and post class with array_unique.
I just check 3.x and array unique is not available in core.
probably sometime in wp 4.x
I hope core team will the also add that in all dynamic classes.
thanks for the information.
I’ll put it in my check list to remove array unique in body class & post class filter.
Ivan says
Hi Bill.
Thanks for the snippet. I was wandering how to do this for a while.
Is there any reason you are not using if()…. elseif() statement rather than going thru each if? the code don’t look like it can get in more than one if statement. That won’t be noticeable speed improvement … but will optimize the code.
Bill Erickson says
There’s no particular reason I used if instead of elseif for the latter conditionals.
Melissa Jean Clark says
This is great! Thanks so much for sharing your code. I’m not as well-versed in filters with WP – I was using a bit of jQuery to accomplish this. This approach is much better!
Pete says
Thanks for this, Bill!
Use this idea on one site. Works great!
Aaron Jerad says
Thanks this worked great. I was using jQuery, but this is much better!
In my case it was likely that menu item titles were going to change. So I used the menu item ID instead, like this:
if( ( is_singular( ‘post’ ) || is_category() || is_tag() ) && 1648 == $item->ID )
$classes[] = ‘current-menu-item’;
I also needed to add a class to the current menu item’s parent or ancestor so simply duplicated the conditional like this:
if( ( is_singular( ‘post’ ) || is_category() || is_tag() || is_singular( ‘custom-post-type ) || is_tax( ‘custom-taxonomy’ ) && 1360 == $item->ID )
$classes[] = ‘current-page-ancestor’;
Sonam says
Hi Aaron –
I’m trying to get this working with genesis and woo-commerce, so on all the sub categories for shop , the shop menu item stays highlighted as current – but no luck so far. Any ideas how to do this?
Thanks
Jim says
Hi Bill
Thanks for sharing. Just trying to work this out.
What is the 10,3 at the end of your code?
I’m trying to figure out how to show “menu-item-74” as selected if is_front_page()
Bill Erickson says
10 is the priority of my filter, and 3 is the number of arguments being passed to my function. More information here.
If you do
print_r( $item );
you can see all the available parameters you can use. I think there’s something like$item->ID
which will have the menu item ID, so you can doif( is_front_page() && $item->ID == 74 )