Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Mark (highlight) custom post type parent as active item in Wordpress Navigation. When you visit a custom post type's single page, the parent menu item (the post type archive) isn't marked as active. This code solves it by comparing the slug of the current post type with the navigation items, and adds a class accordingly.
<?php
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;
// 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';
}
// Return the corrected set of classes to be added to the menu item
return $classes;
}
?>
@juddlyon

This comment has been minimized.

Copy link

juddlyon commented Feb 24, 2014

Nicely done, dropped this into my functions.php and it worked like a charm. Thanks!

@kategee

This comment has been minimized.

Copy link

kategee commented Mar 22, 2014

This is great but

$current_post_type_slug = $current_post_type->rewrite[slug];

should be

$current_post_type_slug = $current_post_type->rewrite['slug'];

(missing brackets)

@rothbert

This comment has been minimized.

Copy link

rothbert commented Apr 8, 2014

Concur with kategee - getting a notice: Use of undefined constant slug - assumed 'slug'
which is fixed by use of single quotes around slug.

Nice snippet - thanks for sharing

@bramwillemse

This comment has been minimized.

Copy link

bramwillemse commented Apr 11, 2014

Great little snippet, thanks!
The only issue I have now is my 'blog' (post type 'posts') menu item getting highlighted too..

@llest

This comment has been minimized.

Copy link

llest commented Apr 16, 2014

The code by gport didn't work for me straight away, but led me to this idea. Thanks!
Add it to your functions.php.

add_action('nav_menu_css_class', 'add_current_nav_class', 10, 2 );
function add_current_nav_class($classes, $item) {
$post_cat = get_the_category();
$post_cat_slug = $post_cat[0]->slug;

if (    ($item->ID == '  38.....   ' && $post_cat_slug == '    ....your posts' categorys' slug here....   ') ||
    ($item->ID == '  37.....  ' && $post_cat_slug == '    ....another posts' categorys' slug here....   ')
    ) {
    $classes[] = 'current-menu-item';
}

return $classes;

}

@egohost

This comment has been minimized.

Copy link

egohost commented Aug 14, 2014

This is my version of basically the same thing, except it does not depend on the slug (Sometimes there can be issues with miss matched slugs)

function custom_active_item_classes($classes = array(), $menu_item = false){            
        global $post;
        $classes[] = ($menu_item->url == get_post_type_archive_link($post->post_type)) ? 'current-menu-item active' : '';
        return $classes;
    }
add_filter( 'nav_menu_css_class', 'custom_active_item_classes', 10, 2 );
@jmage

This comment has been minimized.

Copy link

jmage commented May 26, 2015

Hey thanks! Just had to fix line 12 like this here:

    <?php

      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;

            // 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';

            }

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

        }

    ?>
@xeyefex

This comment has been minimized.

Copy link

xeyefex commented Aug 14, 2015

Thanks for the piece of code !
I would add a check to see if the post ID exist otherwise you would get a (Notice: Trying to get property of non-object) error if no post is found.

So:

  // Mark (highlight) custom post type parent as active item in Wordpress Navigation
  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;

        // 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));
            $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';

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

This comment has been minimized.

Copy link

philiprichter commented Aug 24, 2015

egohosts works like a charm, thanks!

@RayanZenner

This comment has been minimized.

Copy link

RayanZenner commented Jan 13, 2016

Awesome, seems to work right out of the box!

@disturbed-pixel

This comment has been minimized.

Copy link

disturbed-pixel commented Feb 5, 2016

Just like many peeps here, works within seconds on first try! Thanks gerbenvandijk

@scepbv

This comment has been minimized.

Copy link

scepbv commented Feb 23, 2016

Thanks, nice job!

@ndsh

This comment has been minimized.

Copy link

ndsh commented Mar 7, 2016

works like a charm!

@bwjob

This comment has been minimized.

Copy link

bwjob commented Apr 3, 2016

Great job!! Thx!

@XLogus

This comment has been minimized.

Copy link

XLogus commented Apr 19, 2016

Code works good but if post archive is a submenu don`t add class "current_page_parent" to parent menu item

@Kelderic

This comment has been minimized.

Copy link

Kelderic commented Apr 19, 2016

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 );
@rebdev

This comment has been minimized.

Copy link

rebdev commented Apr 24, 2016

Just what I was looking for! I used Kelderic's version (thanks!), but on non-custom-post-type pages I was getting a PHP warning: "Warning: strpos() [function.strpos]: Empty needle in /Applications/AMPPS/www/mysite/wp-content/themes/mytheme/assets/functions/ancestor-menu-classes-for-cpts.php on line 35".

I resolved this by using toscho's is_custom_post_type() function from here, and then only running the main if statement if it's a custom post type we're viewing:

`
// Checking if post ID exist...

if ( isset( $id ) && is_custom_post_type( $id ) ){

`
Maybe someone with a better handle on WordPress needle errors can come up with something better but this stops the warnings.

@frafor1988

This comment has been minimized.

Copy link

frafor1988 commented Jun 20, 2016

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';
        }
@rneto88

This comment has been minimized.

Copy link

rneto88 commented Jun 29, 2016

Great job, working perfectly!! Thanks!

@dag0310

This comment has been minimized.

Copy link

dag0310 commented Sep 21, 2016

This did the trick for me after inserting it into /wp-includes/functions.php (customize lines 5, 6 or more according to your needs):

The key in the mappings array is the category slug and the value is the class name of the corresponding menu item entry.

function mark_menu_item_as_active($classes, $item) {
    $category_menuitem_mappings = array();

    /* !!! This needs to be customized !!! */
    $category_menuitem_mappings['category-slug-1'] = 'menu-item-xx';
    $category_menuitem_mappings['category-slug-2'] = 'menu-item-yy';

    global $post;
    $category_obj = get_the_category($post->ID);
    $category_str = $category_obj[0]->slug;

    foreach ($category_menuitem_mappings as $key => $value) {
        if (in_array($value, $classes) && $category_str === $key) {
            $classes[] = 'current-menu-item';
        }
    }

    return $classes;
}
add_filter('nav_menu_css_class', 'mark_menu_item_as_active', 10, 2);
@nicholasolsen

This comment has been minimized.

Copy link

nicholasolsen commented Nov 6, 2016

Thank you so much! Dropped it in my function file and worked like a charm! HUGE time saver!

@brightspire

This comment has been minimized.

Copy link

brightspire commented Dec 4, 2016

Love your work!

@PlutonW

This comment has been minimized.

Copy link

PlutonW commented Dec 30, 2016

I did the task without comparison URL.
I use the terms ID, it does not depend on your permalink settings.
But you must set your Taxonomy in
$cur_terms = get_the_terms( $post->ID, 'your_taxonomy' );

<?php
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;
		
		// Getting the post type of the current post
		$current_post_type = get_post_type($post->ID);

	if (is_single() && $current_post_type !='post' ) {		
	
		//  Getting the current post taxonomies in prod_cat Taxonomy
		$cur_terms = get_the_terms( $post->ID, 'your_taxonomy' );
		$post_terms_list = array(); 
		$post_terms_parrent_list = array();		
		if ($cur_terms)
			foreach ($cur_terms as $terms) {
				$post_terms_list[] = $terms->term_id;
				if ($terms->parent )
				$post_terms_parrent_list[] = $terms->parent;
			}
							
		// If the menu object ID contains the current post taxonomies ID - add clases
		if (in_array($item->object_id, $post_terms_list)) {		
			$classes[] = 'current-post-ancestor';
		}
		if (in_array($item->object_id, $post_terms_parrent_list)) {		
			$classes[] = 'current-post-ancestor';
			$classes[] = 'current-menu-parent';					
		}

	}		
		// Return the corrected set of classes to be added to the menu item
		return $classes;
}
?>
@wizve

This comment has been minimized.

Copy link

wizve commented Jan 18, 2017

dropped into functions.php and worked great. thanks

@caduvisentin

This comment has been minimized.

Copy link

caduvisentin commented Jan 28, 2017

I add an "else" to remove current_page_parent from blog classes. Like this:

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;
	
	// 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;

}
@WebbizAdmin

This comment has been minimized.

Copy link

WebbizAdmin commented Mar 22, 2017

Worked great! thanks!

@yositomo2

This comment has been minimized.

Copy link

yositomo2 commented Apr 14, 2017

Hi!

I used dag0310 Version. Works fine, only that my starting-page now also highlights a menu-item as active, which it shouldn't...
Any idea?

@maznialwan

This comment has been minimized.

Copy link

maznialwan commented Apr 15, 2017

copy to functions.php and its work well... thanks..

@iceteabottle

This comment has been minimized.

Copy link

iceteabottle commented Apr 18, 2017

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;

}
@tomsnep

This comment has been minimized.

Copy link

tomsnep commented May 12, 2017

Hey guys,

I used this piece of code and it worked like a charm.
Now i made the website multilingual and translated the custom post types slugs (wpml), the piece of code doesn't seem to work anymore when i translated the custom post type slugs.

I'm not really into php so i don't know by myself how i can handle this problem.
Does anyone have suggestions?

Thanks in advance!
Tom

@hiendo3

This comment has been minimized.

Copy link

hiendo3 commented May 13, 2017

Worked great! thanks!

@sandrowuermli

This comment has been minimized.

Copy link

sandrowuermli commented Jun 4, 2017

I modified the code for posts (blog) for single, tag or category etc. too.

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 post if the post type isn't a custom post type
	if ( is_null( $current_post_type_slug ) ) {
		if ( $current_post_type->name === 'post' ) {
			foreach ( explode( '/', $GLOBALS['wp_rewrite']->front ) as $front_url ) {
				if ( $front_url !== '' ) {
					// if slug is '/blog/tipps/' it will pick the first. In this case it's 'blog'
					$current_post_type_slug = $front_url;
					break;
				}
			}
		}
	}

	// 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[] = 'active';
	} else {
		$classes = array_diff( $classes, [ 'current_page_parent' ] );
	}

	// 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 );
@JakeGonzales

This comment has been minimized.

Copy link

JakeGonzales commented Jun 27, 2017

I combined both @egohost and @xeyefex solutions and it seems to be the simplest.

You get the class names on post and custom post type and you also don't get a Trying to get property of non-object error when there are no posts in the archive.

function custom_active_item_classes($classes = array(), $menu_item = false) {
    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 )){
	    $classes[] = ($menu_item->url == get_post_type_archive_link($post->post_type)) ? 'current-menu-item active' : '';
    }

    return $classes;
}
add_filter( 'nav_menu_css_class', 'custom_active_item_classes', 10, 2 );
@VlooMan

This comment has been minimized.

Copy link

VlooMan commented Aug 16, 2017

As reported by @XLogus, If CPT archive is a submenu item, the code does not add class "current_page_parent" to the parent menu item. I have created a new piece of code which highlights the menu parents as well.

The Gist:
https://goo.gl/gxBT3w
https://gist.github.com/VlooMan/e9f49bea6cb3d32c054d7ea05b4845f1

Feel free to post a comment if it helped!

@obaodelana

This comment has been minimized.

Copy link

obaodelana commented Feb 14, 2018

Worked perfectly, Thanks!

@OWMC

This comment has been minimized.

Copy link

OWMC commented Feb 22, 2018

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;  
}  
@harshclimate

This comment has been minimized.

Copy link

harshclimate commented Mar 15, 2018

Thanks! Worked great just pasting it in functions.php!

@markhowellsmead

This comment has been minimized.

Copy link

markhowellsmead commented May 11, 2018

@fuerio

This comment has been minimized.

Copy link

fuerio commented May 12, 2019

Thank you very much! It works great!

@alex-rybachenk0

This comment has been minimized.

Copy link

alex-rybachenk0 commented May 28, 2019

Thanks!

@dangelion

This comment has been minimized.

Copy link

dangelion commented Jun 3, 2019

Someone could put together all these improvements in one final gist? Thanks

@peytonbgregory

This comment has been minimized.

Copy link

peytonbgregory commented Oct 29, 2019

Works perfectly!

@squarecandy

This comment has been minimized.

Copy link

squarecandy commented Dec 14, 2019

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

@ThePixelPixie

This comment has been minimized.

Copy link

ThePixelPixie commented Feb 13, 2020

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.

@squarecandy

This comment has been minimized.

Copy link

squarecandy commented Feb 13, 2020

@ThePixelPixie if you're not using wp_nav_menu() and the built in walker system, this code is not going to help at all.

@ThePixelPixie

This comment has been minimized.

Copy link

ThePixelPixie commented Feb 13, 2020

Ok. Got it. I will see if I can modify my menu design to work with wp_nav_menu() then. THANK you!

@reinisg

This comment has been minimized.

Copy link

reinisg commented Feb 20, 2020

Perfect solution! Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.