Adding a Custom ID to the Body Element in WordPress

In this article we’ll discuss an easy way to add a custom, dynamic ID to the body element on your WordPress Posts and Pages

In a recent article, we discussed how to customize the WordPress body_class() function to add our own custom class to the <body> element on a WordPress Post or Page. In this article we’ll take this functionality one step further and add a custom, dynamic id to the <body> element on a WordPress Post or Page.

First, why add an id to the body element? For one thing, it’s just cool to learn this functionality. For another, adding an id to each page of your site allows for better organizing of your site pages (say, for creating a table of contents), can help increase the efficiency of your JavaScript library (see this article), and the specificity of an id allows for fine‐tuned CSS control in site design.

The Code

First, the code in its entirety:

<?php
// Create a dynamic id on body elements
function get_body_id( $id = '' ) {
    global $wp_query;

    // Fallbacks
    if ( is_front_page() )  $id = 'front-page';
    if ( is_home() )        $id = 'blog';
    if ( is_search() )      $id = 'search';
    if ( is_404() )         $id = 'error404';
        
    // If it's an Archive Page
    if ( is_archive() ) {
        if ( is_author() ) {
            $author = $wp_query->get_queried_object();
            $id = 'archive-author-' . sanitize_html_class( $author->user_nicename , $author->ID );
        } elseif ( is_category() ) {
            $cat = $wp_query->get_queried_object();
            $id = 'archive-category-' . sanitize_html_class( $cat->slug, $cat->cat_ID );
        } elseif ( is_date() ) {
                if ( is_day() ) {
                    $date = get_the_time('F jS Y');
                    $id = 'archive-day-' . str_replace(' ', '-', strtolower($date) );
                } elseif ( is_month() ) {
                    $date = get_the_time('F Y');
                    $id = 'date-' . str_replace(' ', '-', strtolower($date) );   
                } elseif ( is_year() ) {
                    $date = get_the_time('Y');
                    $id = 'date-' . strtolower($date);
                } else {
                    $id = 'archive-date';
                }
        } elseif ( is_tag() ) {
            $tags = $wp_query->get_queried_object();
            $id = 'archive-tag-' . sanitize_html_class( $tags->slug, $tags->term_id );
        } else {
            $id = 'archive';
        }
    }
    
    // If it's a Single Post
    if ( is_single() ) {
        if ( is_attachment() ) {
            $id = 'attachment-'.$wp_query->queried_object->post_name;
        } else {
            $id = 'single-'.$wp_query->queried_object->post_name;
        }
    }

    // If it's a Page
    if ( is_page() ) {
        $id = 'page-'.$wp_query->queried_object->post_name;
        if ('' == $id ) {
            $id = 'page';
        }
    }
    
    // If $id still doesn't have a value, attempt to assign it the Page's name
    if ('' == $id ) {
        $id = $wp_query->queried_object->post_name;
    }
    
    $id = preg_replace("#\s+#", " ", $id);
    $id = str_replace(' ', '-', strtolower($id) );
    
    // Let other plugins modify the function
    return apply_filters( 'body_id', $id );    

};

// Print id on body elements
function body_id( $id = '' ) {
    if ( '' == $id ) {
        $id = get_body_id();
    }
    
    $id = preg_replace("#\s+#", " ", $id);
    $id = str_replace(' ', '-', strtolower($id) );

    echo ( '' != $id ) ? 'id="'.$id. '"': '' ;
};
?>

The Code, Explained

There are two functions in this script; “get_body_id()” and “body_id()”. “get_body_id()” does all the heavy lifting that generates the value of the id attribute, while “body_id()” prints out the value returned by “get_body_id()”.

Now let’s break the script down and explain each section. The first thing we need to do is declare the $wp_query as “global” so we can use its values in our function.

In the first section:

function get_body_id( $id = '' ) {
    global $wp_query;

    // Some general id's
    if ( is_front_page() )  $id = 'front-page';
    if ( is_home() )        $id = 'blog';
    if ( is_search() )      $id = 'search';
    if ( is_404() )         $id = 'error404';

We use WordPress’s conditional tags to check which page we’re on and assign a corresponding id. If we’re on the the front page of the site, assign “front-page” as the id, “blog” if we’re on the main blog index page, “search” if we’re on a search page, and “error404” if we’re on an error page. This will return something that looks like the following:

<body id="front-page" class="...">

In the next section we check to see if we’re on an archive page, and if so, which type of archive page; again, using built‐in WordPress conditional tags:

// If it's an Archive Page
if ( is_archive() ) {
    if ( is_author() ) {
        $author = $wp_query->get_queried_object();
        $id = 'archive-author-' . sanitize_html_class( $author->user_nicename , $author->ID );
    } elseif ( is_category() ) {
        $cat = $wp_query->get_queried_object();
        $id = 'archive-category-' . sanitize_html_class( $cat->slug, $cat->cat_ID );
    } elseif ( is_date() ) {
            if ( is_day() ) {
                $date = get_the_time('F jS Y');
                $id = 'archive-day-' . str_replace(' ', '-', strtolower($date) );
            } elseif ( is_month() ) {
                $date = get_the_time('F Y');
                $id = 'date-' . str_replace(' ', '-', strtolower($date) );   
            } elseif ( is_year() ) {
                $date = get_the_time('Y');
                $id = 'date-' . strtolower($date);
            } else {
                $id = 'archive-date';
            }
    } elseif ( is_tag() ) {
        $tags = $wp_query->get_queried_object();
        $id = 'archive-tag-' . sanitize_html_class( $tags->slug, $tags->term_id );
    } else {
        $id = 'archive';
    }
}

Each archive page body id is prefaced with “archive-”, “type‐of‐archive-”, “archive‐title”, which will return something that looks like:

<body id="archive-category-wordpress-development" class="...">

If we’re on a category page named “WordPress Development”.

The next section deals with single pages (Posts or Attachments):

// If it's a Single Post
if ( is_single() ) {
    if ( is_attachment() ) {
        $id = 'attachment-'.$wp_query->queried_object->post_name;
    } else {
        $id = 'single-'.$wp_query->queried_object->post_name;
    }
}

If it’s a single Post page, it returns the name of the post prefaced with “single-”, like so:

<body id="single-adding-a-custom-id-to-the-body-element-in-wordpress" class="...">

Attachment page ids are returned with the name of the file prefaced with “attachment-”, like so:

<body id="attachment-av12-donor" class="...">

The next section deals with Pages:

// If it's a Page
if ( is_page() ) {
    $id = 'page-'.$wp_query->queried_object->post_name;
    if ('' == $id ) {
        $id = 'page';
    }
}

And will return an id where the name of Page is prefaced with “page-”. So, the About Page body element would look like:

<body id="page-about" class="...">

The next section of the script checks to see if the $id variable is still empty and if so, attempts to create an id using the Post or Page title:

// If $id still doesn't have a value, attempt to assign it the Page's name
if ('' == $id ) {
    $id = $wp_query->queried_object->post_name;
}

This final section filters any spaces in the title and provides a filter hook to allow other plugins to modify the <body> element with custom ids.

// filter any spaces in the title
$id = preg_replace("#\s+#", " ", $id);
$id = str_replace(' ', '-', strtolower($id) );

// Let other plugins modify the function
return apply_filters( 'body_id', $id );

And finally, the body_id() function:

// Print id on body elements
function body_id( $id = '' ) {
    if ( '' == $id ) {
        $id = get_body_id();
    }
    
    $id = preg_replace("#\s+#", " ", $id);
    $id = str_replace(' ', '-', strtolower($id) );

    echo ( '' != $id ) ? 'id="'.$id. '"': '' ;
};

This checks to see if there is a value for the id attribute and if so, it filters any spaces accidentally entered (either manually or a plugin that attaches to the filter hook), and prints out “id="value-of-id"”. If there is no value, it does nothing. This prevents a blank id attribute (id="") from being published.

How It’s Used

Simply add the body_id() tag to the <body> element, which is usually located in the header.php file. It looks like this:

<body <?php body_id(); ?> <?php body_class(); ?>>

As outlined previously, the body_id() tag has the ability to be overridden by custom plugins or manual input. For example, if your page template system has the <body> element in individual template files vs. the header.php file, then you could manually add an id on each like so:

<body <?php body_id('foo'); ?> <?php body_class(); ?>>

Which would return:

<body id="foo" class="...">

If you accidentally entered “foo bar” it will filter the spaces (since ids are one‐word) and replace any single space with a dash (-):

<body id="foo-bar" class="...">

Or you could write a small script to override the default values output by this script. This checks to see if we’re on the About page and if so, sets a custom id:

// create a custom function
function my_custom_body_id($id) {
    if ( is_page('about') )  $id = 'my-custom-about-page-id';
    return $id;
};
add_filter('body_id','my_custom_body_id');

Which returns:

<body id="my-custom-about-page-id" class="...">

Installing The Script

To install, simply copy/paste the code above into your functions.php file and then save the file to your template directory.

Notes & Resources

Note: If you use a page template for your Home page (as I do), your id will look like “<body id="page-TITLE">”, instead of “<body id="front-page">” due to the is_page() conditional check located toward the end of the script. If you’d prefer the id attribute to be “front-page” instead of the title of the page, just replace this:

// If it's a Page
if ( is_page() ) {
    $id = 'page-'.$wp_query->queried_object->post_name;
    if ('' == $id ) {
        $id = 'page';
    }
}

With this:

// If it's a Page and NOT the front Page
if ( is_page() && !is_front_page() ) {
    $id = 'page-'.$wp_query->queried_object->post_name;
    if ('' == $id ) {
        $id = 'page';
    }
}
  • The Codex article of Conditional Tags is located here.
  • Perishable Press has a great article outlining various ways to accomplish a dynamic body id tag located here.

If anyone knows of any other articles outlining another method to carry this out, please let me know and I’ll add it to the list!

First the Article,
Now the Comments

The individual views expressed within are those of the respective commenter and are not reflective of my own or my clients’. Skip to Comment Form →

Nice work. This type of thing should already be part of WordPress core, of course. Too bad it’s not.

@Mike, Thanks! Maybe I should open a Trac ticket, since it does seem like something that should be included automatically.

Thanks you so much !

I just add 5 hours to the required and famous 5 minutes to have a useful website.
I had just given up .. when by chance I found your article.
Mike’s track ticket has been probably closed .. by the time …
I should not be so grumpy now, I know but
I have no affinity to php and .. wordpress whatsoever (I just love Python and Plone) I just got a job to fulfill.

Make Yourself Read, Leave a Comment

Comment moderation is enabled. If you don’t see your comment, either it’s your first time commenting, you’re spam, or there was a glitch. If it’s two of the three, no worries, it’ll show up soon. (* Indicates a required field.)

If you want a commenter avatar, check them out here.

Allowed XHTML: <a> <abbr> <blockquote> <em> <strong>
Enclose code in backticks (`); no need to encode, the filter will do that for you.