Skip to content

Instantly share code, notes, and snippets.

@hearvox
Last active February 23, 2023 16:22
Show Gist options
  • Save hearvox/138586b3741b012c2e9378421f4fb209 to your computer and use it in GitHub Desktop.
Save hearvox/138586b3741b012c2e9378421f4fb209 to your computer and use it in GitHub Desktop.
Add class name for top-level parent category or page to body tag in WordPress posts, pages, and archives.
<?php
/**
* Insert class into body tag for highest level Category or Page.
*
* Pages and Categories with identical slugs get the same class.
* Works on Pages, Posts, and Category achives.
*
* @return string $top_slug Class name for body tag.
*/
function top_cat_or_page_body_class( $class ) {
$prefix = 'topic-'; // Editable class name prefix.
global $wp_query;
$object_id = $wp_query->get_queried_object_id();
$top_slug = ( is_home() ) ? 'home' : 'default';
if ( is_single() ) {
$cats = get_the_category( $object_id ); // Get post categories.
$parents = get_ancestors( $cats[0]->term_id, 'category', 'taxonomy' );
$top_id = ( $parents ) ? end( $parents ) : $object_id;
// If term has parents, get ID of top page.
$top_cat = get_category( $top_id ); // Get top cat object.
$top_slug = $top_cat->slug; // Get top cat slug.
}
if ( is_category() ) {
$parents = get_ancestors( $object_id, 'category', 'taxonomy' );
$top_id = ( $parents ) ? end( $parents ) : $object_id;
// If cat has parents, get ID of top cat.
$top_cat = get_category( $top_id ); // Get top cat object.
$top_slug = $top_cat->slug; // Get top cat slug.
}
if ( is_page() ) {
$parents = get_ancestors( $object_id, 'page', 'post_type' );
$top_id = ( $parents ) ? end( $parents ) : $object_id;
// If page has parents, get ID of top page.
$top_page = get_post( $top_id ); // Get top page object.
$top_slug = $top_page->post_name; // Get top page slug.
}
$class[] = $prefix . $top_slug;
return $class;
}
add_filter( 'body_class', 'top_cat_or_page_body_class' );
/*
// Rewrote the above in 2021. Below is the legacy function:
// Add body-class for top-level parent Page or Category
function topcatpg_body_class( $class ) {
$prefix = 'topic-'; // Editable class name prefix.
$top_cat_pg = 'home'; // Default.
global $top_cat_pg;
// Get class name from top-level Category or Page.
global $wp_query;
if ( is_single() ) {
$wp_query->post = $wp_query->posts[0];
setup_postdata( $wp_query->post );
// Climb Posts category hierarchy, successively replacing
// class name top_cat_pg with slug of higher level cat.
foreach( (array) get_the_category() as $cat ) {
if ( !empty( $cat->slug ) )
$top_cat_pg = sanitize_html_class( $cat->slug, $cat->cat_ID );
while ( $cat->parent ) {
$cat = get_category( (int) $cat->parent);
if ( !empty( $cat->slug ) )
$top_cat_pg = sanitize_html_class( $cat->slug, $cat->cat_ID );
}
}
} elseif ( is_archive() ) {
if ( is_category() ) {
$cat = $wp_query->get_queried_object();
$top_cat_pg = $cat->slug;
// Climb Category hierarchy, successively replacing
// class name with slug of higher level cat.
while ( $cat->parent ) {
$cat = get_category( (int) $cat->parent );
if ( !empty( $cat->slug ) )
$top_cat_pg = sanitize_html_class( $cat->slug, $cat->cat_ID );
}
}
} elseif ( is_page() ) {
global $post;
if ( $post->post_parent ) {
$ancestors = get_post_ancestors( $post->ID );
$root = count( $ancestors ) - 1;
$top_id = $ancestors[$root];
$top_pg = get_page( $top_id );
$top_cat_pg = $top_pg->post_name;
} else {
$top_cat_pg = $post->post_name;
}
}
$class[] = $prefix . $top_cat_pg;
return $class;
}
// Uncomment to use this function instead ot the above newer one:
// add_filter( 'body_class', 'topcatpg_body_class' );
*/
?>
@buzi
Copy link

buzi commented May 15, 2018

Thanks !!

@claytonschase
Copy link

Awesome! Thank you!

@seangreen
Copy link

Perfect! Just works... ;)

@gmilic
Copy link

gmilic commented Sep 16, 2019

Thank you very much!!!
Works perfectly!

@vinillab
Copy link

Thanks for sharing this! Just what I needed :)

@maryannk
Copy link

Thank you - works great and just what I need.

@thesunshade
Copy link

I was getting the message

Notice: Only variables should be assigned by reference in xxxxx /functions.php on line 112

Which was $cat = &get_category( (int) $cat->parent);

Removing the & seems to have fixed it.

Any advice? Is that really the proper fix?

@hearvox
Copy link
Author

hearvox commented Sep 2, 2021

@thesunshade , You're correct, the "&" was a mistake, now removed. But just added a new, improved, much leaner function, top_cat_or_page_body_class(). (It's not yet as rigorously tested on a production site as the old was, but it's working well on a dev site.)

@thesunshade
Copy link

But just added a new, improved, much leaner function

I'm happy to test it out. Where can I find it?

@hearvox
Copy link
Author

hearvox commented Sep 2, 2021

It's in this same gist above: two functions in there now. The new one comes first, top_cat_or_page_body_class(). (Kept the old one in there, for now, in case anyone needs to find it. I'll comment it out to make that clearer.)

@thesunshade
Copy link

Oh. It still has the &get_category. That's why I was confused.

@hearvox
Copy link
Author

hearvox commented Sep 2, 2021

Ooops, I didn't notice there were two. Now both fixed (in the older, commented-out function). Thanks much, @thesunshade .

@thesunshade
Copy link

Thank you! I really appreciate you responding so quickly. And I appreciate you sharing the script.

Now I just have to remember exactly what this script was doing on my site in the first place. :-)

@sambieluy
Copy link

This code works SO well, and I really want to keep using it, but I seem to be getting daily errors in my php_errorlog:

[20-Feb-2023 20:17:03 UTC] PHP Warning: Attempt to read property "slug" on null in /home/customer/www/[website folder]/public_html/wp-content/themes/generatepress_child/functions.php on line 91

It's referring to the first instance of $top_slug = $top_cat->slug; // Get top cat slug.

Is there something I can do prevent the errors? The code is working perfectly otherwise!

@hearvox
Copy link
Author

hearvox commented Feb 21, 2023

@sambieluy
None of the sites I work with are using this script now. So I'm just guessing, not testing, but maybe I need to wrap that error-producing line in an if, to trap for get_category( $top_id ) not returning a category (because, if it did, the cat would have a slug), like:
$top_slug = ($top_cat) ? $top_cat->slug : $top_slug;

If you have a chance to test that, please do. Tell me if it gets rid of the errors.

@sambieluy
Copy link

Thanks so much for your response, @hearvox :)

I tried changing $top_slug = $top_cat->slug to if ( $top_slug = $top_cat->slug ); yesterday (Feb. 21st), and it seems like 3 more of the same errors came through today. Interestingly, there are way fewer errors this time, but I'm not sure if that's relevant.

[22-Feb-2023 16:42:41 UTC] PHP Warning: Attempt to read property "slug" on null in /home/customer/www/[website folder]/public_html/wp-content/themes/generatepress_child/functions.php on line 91
[22-Feb-2023 16:45:23 UTC] PHP Warning: Attempt to read property "slug" on null in /home/customer/www/[website folder]/public_html/wp-content/themes/generatepress_child/functions.php on line 91
[22-Feb-2023 18:40:26 UTC] PHP Warning: Attempt to read property "slug" on null in /home/customer/www/[website folder]/public_html/wp-content/themes/generatepress_child/functions.php on line 91

Could it be because I implemented the "if" statement incorrectly (screenshot attached for more context)? Do I need to have some kind of dependent condition afterwards? Really sorry for my lack of knowledge with PHP!

Screenshot 2023-02-22 at 16 07 17

@sambieluy
Copy link

@hearvox Just a quick update that we're back to the same number of "Attempt to read property 'slug' on null" errors today (16 instances so far today), so ignore what I said about there being fewer errors. It must be based on how many users cause the code to run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment