-
-
Save gerbenvandijk/5253921 to your computer and use it in GitHub Desktop.
Great script. I also had a problem with the Blog menu highlighting when on a single CPT post. @iceteabottle's 'else' statement dealt with that but left another problem... Now the Blog menu no longer highlights even when on a single blog post.
The fix was to add an 'if' statement to check to make sure we aren't on a single blog post before running the code.
add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 );
function add_current_nav_class($classes, $item) {
// Getting the current post details
global $post;
// Make sure we're not on a single blog post before running the code...
if ( !is_singular( 'post' ) ) {
// Getting the post type of the current post
$current_post_type = get_post_type_object(get_post_type($post->ID));
$current_post_type_slug = $current_post_type->rewrite['slug'];
// Getting the URL of the menu item
$menu_slug = strtolower(trim($item->url));
// If the menu item URL contains the current post types slug add the current-menu-item class
if (strpos($menu_slug,$current_post_type_slug) !== false) {
$classes[] = 'current-menu-item';
}
// as we are not on a single blog post, stop blog menu from highlighting
else {
$classes = array_diff( $classes, array( 'current_page_parent' ) );
}
}
// Return the corrected set of classes to be added to the menu item
return $classes;
}
Thanks! Worked great just pasting it in functions.php!
Improvement to cover standard posts too: https://gist.github.com/markhowellsmead/5aeb12903281f196af9678dc1cf87e20
Thank you very much! It works great!
Thanks!
Someone could put together all these improvements in one final gist? Thanks
Works perfectly!
Here's what I did to make sure I grabbed the parent item too:
https://gist.github.com/squarecandy/19bcc3faf6552f29bac04670e4b081c4
This only works for one level of depth, but it works if the parent URL is completely different from the post type slug
I'm wondering if there's a way to modify this for taxonomy page parent and children in a non-WP-standard nav? In other words, I have a nav system built using Advanced Custom Fields, querying taxonomies and displaying random images within that taxonomy. But the ACF area allows the user to select several taxonomy terms as the menu items. So my menu is not built using the WP walker. I just need to add "active" to parent links when on children pages, and the jQuery I have is only working for parent pages.
@ThePixelPixie if you're not using wp_nav_menu() and the built in walker system, this code is not going to help at all.
Ok. Got it. I will see if I can modify my menu design to work with wp_nav_menu() then. THANK you!
Perfect solution! Thanks!
This worked great for 90% of my links. Still having trouble with WooCommerece though. When I go to /cart or /checkout, my /shop menu item doesn't have an active class.
Thank you! This saved me so much trouble.
Below is the type-safe code, this will have all the guards needed to avoid any warning or error.
add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 );
function add_current_nav_class($classes, $item) {
if ( ! ( $item instanceof WP_Post ) ) return $classes;
$post = get_post();
if ( empty( $post ) ) return $classes;
$post_type = get_post_type( $post->ID );
$post_type_object = get_post_type_object( $post_type );
if ( ! ( $post_type_object instanceof WP_Post_Type ) || ! $post_type_object->has_archive ) return $classes;
$post_type_slug = $post_type_object->rewrite['slug'];
$menu_slug = strtolower( trim( $item->url ) );
if ( empty( $post_type_slug ) || empty( $menu_slug ) ) return $classes;
if ( strpos( $menu_slug, $post_type_slug ) === false ) return $classes;
$classes[] = 'current-menu-item';
return $classes;
}
I added some error handling...
add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 ); function add_current_nav_class($classes, $item) { // Getting the current post details global $post; // check if it's a post if (empty($post)) { return $classes; } // Getting the post type of the current post $current_post_type = get_post_type_object(get_post_type($post->ID)); $current_post_type_slug = $current_post_type->rewrite['slug']; // Getting the URL of the menu item $menu_slug = strtolower(trim($item->url)); // If the menu item URL contains the current post types slug add the current-menu-item class if (strpos($menu_slug,$current_post_type_slug) !== false) { $classes[] = 'current-menu-item'; } else { $classes = array_diff( $classes, array( 'current_page_parent' ) ); } // Return the corrected set of classes to be added to the menu item return $classes; }
Thank you this helped me a lot get rid of the errors 👍
Had same problem as @rebdev solved by changing:
// If the menu item URL contains the post type's parent if (strpos($menu_slug,$parent) !== false) { $classes[] = 'current-menu-item'; }
to:
// If the menu item URL contains the post type's parent if (!empty($menu_slug) && !empty($parent) && strpos($menu_slug,$parent) !== false) { $classes[] = 'current-menu-item'; }
Thanks for the fix and thanks to @Kelderic for the code.
Its exactly what i was looking for. It works like a charm.
But i noticed a funny error. I have a slug for a CPT called "games" and my domain contains the word "games". So the code does always find the slug and highlights every page and mark every menu item as anchestor. So i wrote a little fix. I just remove the domain from the menu slug. It works now perfectly.
function add_current_nav_class($classes, $item) {
// Getting the current post details
global $post;
// Get post ID, if nothing found set to NULL
$id = ( isset( $post->ID ) ? get_the_ID() : NULL );
// Checking if post ID exist...
if (isset( $id )){
// Getting the post type of the current post
$current_post_type = get_post_type_object(get_post_type($post->ID));
// Getting the rewrite slug containing the post type's ancestors
$ancestor_slug = $current_post_type->rewrite['slug'];
// Split the slug into an array of ancestors and then slice off the direct parent.
$ancestors = explode('/',$ancestor_slug);
$parent = array_pop($ancestors);
// Getting the URL of the menu item
$menu_slug = strtolower(trim($item->url));
// Remove domain from menu slug
$menu_slug = str_replace($_SERVER['SERVER_NAME'], "", $menu_slug);
// If the menu item URL contains the post type's parent
if (!empty($menu_slug) && !empty($parent) && strpos($menu_slug,$parent) !== false) {
$classes[] = 'current-menu-item';
}
// If the menu item URL contains any of the post type's ancestors
foreach ( $ancestors as $ancestor ) {
if (strpos($menu_slug,$ancestor) !== false) {
$classes[] = 'current-page-ancestor';
}
}
}
// Return the corrected set of classes to be added to the menu item
return $classes;
} add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 );
I had a problem when the rewrite slug is multiple levels, ie 'support/downloads'. I edited the code to fix this, and allow for unlimited levels, along with the ID protection that xeyefex added.
function add_current_nav_class($classes, $item) { // Getting the current post details global $post; // Get post ID, if nothing found set to NULL $id = ( isset( $post->ID ) ? get_the_ID() : NULL ); // Checking if post ID exist... if (isset( $id )){ // Getting the post type of the current post $current_post_type = get_post_type_object(get_post_type($post->ID)); // Getting the rewrite slug containing the post type's ancestors $ancestor_slug = $current_post_type->rewrite['slug']; // Split the slug into an array of ancestors and then slice off the direct parent. $ancestors = explode('/',$ancestor_slug); $parent = array_pop($ancestors); // Getting the URL of the menu item $menu_slug = strtolower(trim($item->url)); // If the menu item URL contains the post type's parent if (strpos($menu_slug,$parent) !== false) { $classes[] = 'current-menu-item'; } // If the menu item URL contains any of the post type's ancestors foreach ( $ancestors as $ancestor ) { if (strpos($menu_slug,$ancestor) !== false) { $classes[] = 'current-page-ancestor'; } } } // Return the corrected set of classes to be added to the menu item return $classes; } add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 );
This solution is 99% there for me, but doesn't quite work when there are other items that may share part of the CPT slug in their slugs. For example, the slug for my CPT is /my-hr/knowledge/....
In our Nav, we have the main My HR item, but then this has a couple of sub pages in a drop down.
My HR
-- Contact HR
-- Employee Benefits
With this above function, both Contact and Employee benefits show active as well as My HR when viewing a CPT post.
Anyone know a workaround for this?
Thanks for sharing!
Further fixes I came up with:
Added a fallback to empty string if rewrite
is false
$ancestor_slug = $current_post_type->rewrite ? $current_post_type->rewrite['slug'] : '';
Check for $menu_slug
& $anchestor
if (!empty($menu_slug) && !empty($ancestor) && strpos($menu_slug,$ancestor) !== false) {
Result:
function add_current_nav_class($classes, $item) {
// Getting the current post details
global $post;
// Get post ID, if nothing found set to NULL
$id = ( isset( $post->ID ) ? get_the_ID() : NULL );
// Checking if post ID exist...
if (isset( $id )){
// Getting the post type of the current post
$current_post_type = get_post_type_object(get_post_type($post->ID));
// Getting the rewrite slug containing the post type's ancestors
$ancestor_slug = $current_post_type->rewrite ? $current_post_type->rewrite['slug'] : '';
// Split the slug into an array of ancestors and then slice off the direct parent.
$ancestors = explode('/',$ancestor_slug);
$parent = array_pop($ancestors);
// Getting the URL of the menu item
$menu_slug = strtolower(trim($item->url));
// Remove domain from menu slug
$menu_slug = str_replace($_SERVER['SERVER_NAME'], "", $menu_slug);
// If the menu item URL contains the post type's parent
if (!empty($menu_slug) && !empty($parent) && strpos($menu_slug,$parent) !== false) {
$classes[] = 'current-menu-item';
}
// If the menu item URL contains any of the post type's ancestors
foreach ( $ancestors as $ancestor ) {
if (!empty($menu_slug) && !empty($ancestor) && strpos($menu_slug,$ancestor) !== false) {
$classes[] = 'current-page-ancestor';
}
}
}
// Return the corrected set of classes to be added to the menu item
return $classes;
}
add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 );
@mmoollllee (and others) thanks! I updated the original gist 👍
Thank you. Exactly what I needed! :) 👍
Thank you, this is working great:) I'm surprised Wordpress doesn't do that out of the box...
Great Gist, thanks to everyone for sharing! Saved me a lot of time!
It did however not work for me with child pages that are an ancestor of a menu item.
I think it's because pages don't have a rewrite slug.
So I checked for pages and populated the ancestors array accordingly
if ( $current_post_type->name == 'page'){
$ancestors = array();
foreach ( get_post_ancestors($post) as $ancestor ) {
$ancestors[] = get_post_field( 'post_name', $ancestor );
}
}
This is the full snippet:
function add_current_nav_class($classes, $item) {
// Getting the current post details
global $post;
// Get post ID, if nothing found set to NULL
$id = ( isset( $post->ID ) ? get_the_ID() : NULL );
// Checking if post ID exist...
if (isset( $id )){
// Getting the post type of the current post
$current_post_type = get_post_type_object(get_post_type($post->ID));
//var_dump($current_post_type->name);
// Getting the rewrite slug containing the post type's ancestors
$ancestor_slug = $current_post_type->rewrite ? $current_post_type->rewrite['slug'] : '';
// Split the slug into an array of ancestors and then slice off the direct parent.
$ancestors = explode('/',$ancestor_slug);
// Pages have no rewrite base, so check for parent pages and populate array with all slugs of parents
if ( $current_post_type->name == 'page'){
$ancestors = array();
foreach ( get_post_ancestors($post) as $ancestor ) {
$ancestors[] = get_post_field( 'post_name', $ancestor );
}
}
$parent = array_pop($ancestors);
// Getting the URL of the menu item
$menu_slug = strtolower(trim($item->url));
// Remove domain from menu slug
$menu_slug = str_replace($_SERVER['SERVER_NAME'], "", $menu_slug);
// If the menu item URL contains the post type's parent
if (!empty($menu_slug) && !empty($parent) && strpos($menu_slug,$parent) !== false) {
$classes[] = 'current-menu-item';
}
// If the menu item URL contains any of the post type's ancestors
foreach ( $ancestors as $ancestor ) {
if (!empty($menu_slug) && !empty($ancestor) && strpos($menu_slug,$ancestor) !== false) {
$classes[] = 'current-page-ancestor';
}
}
}
// Return the corrected set of classes to be added to the menu item
return $classes;
}
add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 );
Worked perfectly, Thanks!