Custom post type singular menu class active

Hi,

I have trouble with menu on my custom post type.

  1. I have custom post type: Portfolio
  2. I add portfolio as posts
  3. When I open single portfolio item, in menu is my blog page marked as active.

I have managed to add active class to my menu-portfolio, but I can’t remove ‘active’ from menu-blog item.

Code to add class to custom menu from wp examples:

function my_special_nav_class( $classes, $item )
{
    if( is_single() && $item->title == 'Blog' )
    {
        $classes[] = 'special-class';
    }
    return $classes;
}
add_filter( 'nav_menu_css_class', 'my_special_nav_class', 10, 2 );

Another example is, where I can filter class but this doesn’t work with multiple post types.
It always adds active class to first item:

// Remove active class from menu
function remove_active_class($class) {
    return ( $class == 'active' ) ? FALSE : TRUE;
}

// Add active class to menu of post type single template
function add_class_to_wp_nav_menu($classes) {
    switch ( get_post_type() ) {
      case 'portfolio':

        $classes = array_filter( $classes, 'remove_active_class' );

        if( in_array( 'menu-portfolio', $classes) ) {
          $classes[] = 'active';
        }

     }
  return $classes;
}
add_filter('nav_menu_css_class', 'add_class_to_wp_nav_menu');

Hm, again me replying to my own question.
For some reason ‘switch’ is not working but ‘elseif’ is working fine.

Here is an working example:

// Remove active class from menu
function remove_active_class($class) {
    return ( $class == 'active' ) ? FALSE : TRUE;
}

// Add active class to menu of post type single template
function add_class_to_wp_nav_menu($classes) {

    if( is_singular( 'portfolio' ) ) {

        $classes = array_filter( $classes, 'remove_active_class' );

        if( in_array( 'menu-portfolio', $classes) ) {
          $classes[] = 'active';
        }
    } elseif( is_singular( 'resources' ) ) {

        $classes = array_filter( $classes, 'remove_active_class' );

        if( in_array( 'menu-resources', $classes) ) {
          $classes[] = 'active';
        }
    }

  return $classes;
}
add_filter('nav_menu_css_class', 'add_class_to_wp_nav_menu');

Here’s an example using switch that is straight from the roots.io codebase:

<?php
/**
 * Fix wp_nav_menu's active item highlighting with custom post types
 */
function roots_cpt_active_menu($menu) {
  $post_type = get_post_type();

  switch($post_type) {
    case 'gallery':
      $menu = str_replace('active', '', $menu);
      break;
    case 'theme':
      $menu = str_replace('active', '', $menu);
      $menu = str_replace('menu-themes', 'menu-themes active', $menu);
      break;
    case 'screencast':
      $menu = str_replace('active', '', $menu);
      $menu = str_replace('menu-screencasts', 'menu-screencasts active', $menu);
      break;
    case 'plugin':
      $menu = str_replace('active', '', $menu);
      $menu = str_replace('menu-plugins', 'menu-plugins active', $menu);
      break;
  }

  if (is_author()) {
    $menu = str_replace('active', '', $menu);
  }

  return $menu;
}
add_filter('nav_menu_css_class', 'roots_cpt_active_menu', 400);
1 Like

Thanks Ben, will add this to my snippets.

Cheers

I think I’m suffering from the same problem. Whenever I add a new custom post type to my navigation menu, both it and the nav item for my main posts page get the .active class. I’ve tried Ben’s code and that worked for me but it’s not very scalable/elegant if I have to keep adding my custom post types to that switch statement. Anyone have a better solution?

Thanks,
Graham

I ignore the parent/child hierarchy determined by the pages menu (which is affected by a WordPress bug) and instead rely only on the menu hierarchy. It’s easily done by replacing the roots_nav_menu_css_class with the following:

function roots_nav_menu_css_class($classes, $item) {
  $slug = sanitize_title($item->title);
  $classes = preg_replace('/(current(-menu-)(item|parent))/', 'active', $classes);
  $classes = preg_replace('/(current(-menu-|[-_]page[-_])(parent|ancestor))/', '', $classes);
  $classes = preg_replace('/^((menu|page)[-_\w+]+)+/', '', $classes);

  $classes[] = 'menu-' . $slug;
    
  $classes = array_unique($classes);

  return array_filter($classes, 'is_element_empty');
}
2 Likes

This is nicer but it only half fixes the issue for me because when I’m on a single custom post page the nav item for that post type is missing the .current_page_parent class so it’s not getting picked up by the filter.

BTW, I really appreciate all the input guys, I’m picking this stuff up as I go along!

I’ve been working on a fix to this but it needs some testing. It would be great if you could implement the changes in this branch and let me know how you fare.

Working on a new project, will test it and come back with results in few days.

@Foxaii, I’m getting a Notice: Undefined property: Roots_Nav_Walker::$active

It’s a long time since this topic but, did somebody found a new solution that not implies to edit the function for every custom post type? Something more like ‘drag&drop’ function. This wordpress ‘bug’ with the archives and menus is really annoying and has remained in wordpress updates, I can’t understand why. Anyway, roots snippets solutions works well, but I was curious about new approaches or solutions you probably have found guys. Thank you!

The fix in the nav_rejig branch works. We just need to decide what we are doing with the functions that it requires but were moved to soil.

Hey all I’m also experiencing the same issue with a CPT archive page. I tried overwriting my nav.php with the /nav_rejig's nav.php but apparently there’s no function definition for roots_url_compare() and that causes a fatal error. I’d love to be able to implement your elegant solution instead of a janky switch statement search + replace or something. Please let me know how to use the new code and sorry if I’ve overlooked the proper way to use it.

Thanks for your work!

Lots of changes since I created that branch, but I imagine the fix still works. The function you are looking for should be in utils.php on the same branch.

Thanks @Foxaii! For some reason github’s search wasn’t finding that func definition when I looked for it before…

Also, just in case anyone else is trying to implement this, you’ll also need to include the relative-urls.php file in /lib to get it to work. Looking forward to 8.0!

I tried doing what @caliper suggested and now on the pages based on CPT’s, I end up with blank pages with a 500 error returned from the server when I look at the console. Not sure what’s happening.

Use 8.0.0, find out how to put the puzzle pieces together for the older version, or use one of the snippets from earlier in this thread.

Closing this thread since it’s old, has several solutions, and is no longer relevant.