开发者

Wordpress custom post type hierarchy and menu highlighting (current_page_parent)

I have created a custom post type of 'portfolio' and page with a template that retrieves all posts matching that custom post type.

The problem is when I drill down into the actual post, the post seems to sit under 'blog' in the main menu highlighting (displays current_page_parent as a class)

The permalink url is correct: www.site.com/portfolio/post-slug

But the menu thinks the parent is 'blog'.

This is o开发者_运维百科bviously a hierarchical issue but I don't know what to do to fix it.


It appears this is an issue with the core Wordpress code; the code that generates the menu classes adds current_page_parent to your Blog page everywhere except when viewing static page templates.

(This has been discussed in passing at http://core.trac.wordpress.org/ticket/13543).

You can however get around this with some custom code using the page_css_class filter. For example, add something along these lines to functions.php (not 100% tested):

function my_page_css_class($css_class, $page) {
    if (get_post_type()=='portfolio' || is_page(57)) {
        if ($page->ID == get_option('page_for_posts')) {
            foreach ($css_class as $k=>$v) {
                if ($v=='current_page_parent') unset($css_class[$k]);
            }
        }
        if ($page->ID==57) {
            $css_class[]='current_page_parent';
        }
    }
    return $css_class;
}
add_filter('page_css_class','my_page_css_class',10,2);

Replacing 57 with the ID of your portfolios page, of course. That removes current_page_parent when printing the blog page and adds current_page_parent to your portfolios page, when either viewing a single portfolio or viewing the portfolios page itself.


Here is my optimized/extended version of previously suggested solutions, which is pretty much fully automated. No more extra CSS or menu attributes needed.

This version dynamically gets a list of custom post types and if the current post type is a custom post type, then it removes the 'current_page_parent' class from all menu items.

Furthermore it checks each menu item to see if it's for a page with a page template like "page-{custom_post_type_slug}.php", and if so, it'll add the 'current_page_parent' class.

The filter priority is 1, as some themes, replace the current_page_parent/etc. classes with a class like 'active' (eg. 'roots' does this), so this filter needs to execute first.

Lastly, it makes use of 3 static variables since this function is repeatedly called and these (obviously) remain the same through all calls.

function theme_current_type_nav_class($css_class, $item) {
    static $custom_post_types, $post_type, $filter_func;

    if (empty($custom_post_types))
        $custom_post_types = get_post_types(array('_builtin' => false));

    if (empty($post_type))
        $post_type = get_post_type();

    if ('page' == $item->object && in_array($post_type, $custom_post_types)) {
        $css_class = array_filter($css_class, function($el) {
            return $el !== "current_page_parent";
        });

        $template = get_page_template_slug($item->object_id);
        if (!empty($template) && preg_match("/^page(-[^-]+)*-$post_type/", $template) === 1)
            array_push($css_class, 'current_page_parent');

    }

    return $css_class;
}
add_filter('nav_menu_css_class', 'theme_current_type_nav_class', 1, 2);

PS. Just to point out one shortcoming in all non-CSS solutions I've seen so far, including my own: Something not taken into account is highlighting the menu item parent/ancestor of an item linking to a page which displays posts of the current custom post type. Consider a custom post type "product" and a menu like:

Home  Company  News  Contact
      |
      \--About Us
      \--Products

"Products" is a page with a template "page-product.php" and shows an overview of posts of type 'product'. It is highlighted due to posted solution. However 'Company' as its parent/ancestor should also be highlighted, but isn't. Something to keep in mind.


WP ticket: http://core.trac.wordpress.org/ticket/16382

function fix_blog_menu_css_class( $classes, $item ) {
    if ( is_tax( 'my-cat-tax' ) || is_singular( 'my-post-type' ) || is_post_type_archive( 'my-post-type' ) ) {
        if ( $item->object_id == get_option('page_for_posts') ) {
            $key = array_search( 'current_page_parent', $classes );
            if ( false !== $key )
                unset( $classes[ $key ] );
        }
    }

    return $classes;
}
add_filter( 'nav_menu_css_class', 'fix_blog_menu_css_class', 10, 2 );


I did some more looking around on this and found another way of doing this.

add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2);
function current_type_nav_class($css_class, $item)
{
    if (get_post_type() === 'portfolio') {
        $current_value = 'current_page_parent'; 
        $css_class = array_filter($css_class, function ($element) use ($current_value) {
            return ($element != $current_value);
        });
    }

    $post_type = get_query_var('post_type');
    if ($item->attr_title !== '' && $item->attr_title === $post_type) {     
        array_push($css_class, 'current_page_parent');
    };

    return $css_class;
}

I got some help form this post and then modified it to also remove the "current_page_parent" class from the blog page. https://wordpress.stackexchange.com/questions/3014/highlighting-wp-nav-menu-ancestor-class-w-o-children-in-nav-structure/3034#3034

Cordially Vayu


As explained at https://core.trac.wordpress.org/ticket/16382, .current_page_parent matches "anything that isn't a page" for the sake of backwards compatibility (bear in mind that this was considered backwards 10 years ago...) so themes nowadays really shouldn't still be using it.

So the simplest solution, and the most efficient (since unlike previous answers it doesn't require running extra code on every page load), is to modify your theme's CSS to replace use of the .current_page_parent class selector with .current-menu-parent, which does the right thing. (NB underscores vs hyphens.)

If you are using a third-party theme and don't want to modify it directly, then you can overwrite its properties in your own stylesheet. For example, if your theme has:

.current_page_parent > a {
    border-bottom: 4px solid blue;
}

then in your child theme's stylesheet you would do this to cancel out its effects, and apply them to the correct class:

.current_page_parent > a {
    border-bottom: transparent !important;  /* Cancel out incorrect styling */
}
.current-menu-parent > a {
    border-bottom: 4px solid blue;   /* Add styling correctly */
}

This is just an example - the correct way will depend on how your theme is styling these links.


Here is a solution that worked for me, without having to define my custom post type or menu id or page id in the code:

http://dtbaker.net/web-development/how-to-stop-wordpress-automatically-highlighting-the-blog-page-in-the-menu/

function dtbaker_wp_nav_menu_objects($sorted_menu_items, $args){
    // this is the code from nav-menu-template.php that we want to stop running
    // so we try our best to "reverse" this code wp code in this filter.
    /* if ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id )
            $classes[] = 'current_page_parent'; */

    // check if the current page is really a blog post.
    //print_r($wp_query);exit;
    global $wp_query;
    if(!empty($wp_query->queried_object_id)){
        $current_page = get_post($wp_query->queried_object_id);
        if($current_page && $current_page->post_type=='post'){
            //yes!
        }else{
            $current_page = false;
        }
    }else{
        $current_page = false;
    }


    $home_page_id = (int) get_option( 'page_for_posts' );
    foreach($sorted_menu_items as $id => $menu_item){
        if ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id ){
            if(!$current_page){
                foreach($sorted_menu_items[$id]->classes as $classid=>$classname){
                    if($classname=='current_page_parent'){
                        unset($sorted_menu_items[$id]->classes[$classid]);
                    }
                }
            }
        }
    }
    return $sorted_menu_items;
}
add_filter('wp_nav_menu_objects','dtbaker_wp_nav_menu_objects',10,2);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜