-1

I know there is a body_class function for WordPress. But is there one (or a way) to add a class to the HTML element?

My goal is to be able to add a unique class (or ID) to a page's HTML element. Currently my theme is adding the page-id-XXXX class to the body element, but I need a way to have a unique class or ID on the actual HTML element. I'd probably be fine having the page-id-XXXX also added onto the HTML element, although I'd prefer being able to have a custom field that's added to each page, where I can type in the class/ID that would then get added to the HTML element.

At the very least, is there a function I can use to add a class or ID to the HTML element, similar to how the body_class function works?

  • Would you mind explaining why you need to do this? (I'm just curious.) Also, is a JavaScript solution acceptable, or would you prefer a PHP approach? – Dave Romsey May 27 '17 at 23:52
  • 1
    @DaveRomsey my main reason for this is I'm creating a custom one-off page on an existing WordPress website. It's a page that will be used for a 4K TV Slideshow. The current theme sets CSS of font-size and line-height on the <html> element. Additionally, some of the theme's content styling uses rem for the font size. Essentially, I'd like to drastically increase the font-size and line-height of the HTML element — for this page only — so that I can continue using rem sizing (since rem works from the HTML sizes). – Garconis May 28 '17 at 1:27
  • I would definitely prefer PHP over JS. For now I'm just using internal CSS to change the HTML element sizes, but would prefer to add the CSS to the main external CSS file... hence the desire to be able to target this specific page's HTML element. – Garconis May 28 '17 at 1:30
  • Check this answer it solved my problem. – hayatbiralem Sep 5 '17 at 7:57
1

WordPress does not have an equivalent of body_class() for the html tag.

Here's an approach that uses output buffering to capture the final output of the HTML document being rendered. The output buffering code below was adapted from solutions posted here and here. This allows us to access the final output of the HTML document which we then parse and edit and is done without needing to modify the theme's template files.

First, we start output buffering and wire up our shutdown function, which works by iterating through all the open buffer levels, closing them and capturing their output. It then fires off the wpse_final_output filter, echoing the filtered content.

/**
 * Start output buffering
 */
add_action( 'wp', 'wpse_ob_start' );
function wpse_ob_start() {
    // Bail immediately if this is the admin area.
    if ( is_admin() ) {
        return;
    }

    // Bail immediately if this is a feed.
    if ( is_feed() ) {
        return;
    }

    // Start output buffering.
    ob_start();
    add_action( 'shutdown', 'wpse_ob_clean', 0 );
}

/**
 * Ensure the buffer is clean and then trigger the wpse_final_output filter.
 * This fires right before WP's similar shutdown functionality.
 */
function wpse_ob_clean() {
    $final = '';

    // We'll need to get the number of ob levels we're in, so that we can
    // iterate over each, collecting that buffer's output into the final output.
    $levels = ob_get_level();

    for ( $i = 0; $i < $levels; $i++ ) {
        $final .= ob_get_clean();
    }

    // Apply any filters to the final output
    echo apply_filters( 'wpse_final_output', $final );
}

Here the HTML is parsed and the custom filter wpse_additional_html_classes is triggered which lets us add our additional classes in a separate function. This code is a bit verbose, but it covers several edge cases that I've run into when using DOMDocument to parse HTML.

add_filter( 'wpse_final_output', 'wpse_html_tag', 10, 1 );
/**
 * Parse final buffer output. Triggers wpse_additional_html_classes, which 
 * allows us to add classes to the html element.
 */
function wpse_html_tag( $output ) {

    // Filterable list of html classes.
    $additional_html_classes = apply_filters( 'wpse_additional_html_classes', array() );

    // Bail if there are no classes to add since we won't need to do anything.
    if ( ! $additional_html_classes ) {
        return $output;
    }

    // Create an instance of DOMDocument.
    $dom = new \DOMDocument();

    // Suppress errors due to malformed HTML.
    // See http://stackoverflow.com/a/17559716/3059883
    $libxml_previous_state = libxml_use_internal_errors( true );

    // Populate $dom with buffer, making sure to handle UTF-8, otherwise
    // problems will occur with UTF-8 characters.
    // Also, make sure that the doctype and HTML tags are not added to our HTML fragment. http://stackoverflow.com/a/22490902/3059883
    $dom->loadHTML( mb_convert_encoding( $output, 'HTML-ENTITIES', 'UTF-8' ), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );

    // Restore previous state of libxml_use_internal_errors() now that we're done.
    // Again, see http://stackoverflow.com/a/17559716/3059883
    libxml_use_internal_errors( $libxml_previous_state );

    // Create an instance of DOMXpath.
    $xpath = new \DOMXpath( $dom );

    // Get the first html element.
    $html = $xpath->query( "/descendant::html[1]" );

    // Get existing classes for html element.
    $definedClasses = explode( ' ', $dom->documentElement->getAttribute( 'class' ) );

    // Adds our additional classes to the existing classes. Ensure that proper spacing of class names
    // is used and that duplicate class names are not added.
    foreach ( $html as $html_tag ) {
        $spacer = ' ';
        // Spacer will be set to an empty string if there are no existing classes.
        if ( isset( $definedClasses[0] ) && false == $definedClasses[0] ) {
            $spacer = '';
        }       
        foreach ( $additional_html_classes as $additional_html_class ) {
            if ( ! in_array( $additional_html_class , $definedClasses ) ) {
                $html_tag->setAttribute(
                    'class', $html_tag->getAttribute( 'class' ) . $spacer . $additional_html_class
                );
            }
            $spacer = ' ';
        }
    }

    // Save the updated HTML.
    $output = $dom->saveHTML();     

    return $output;
}

The wpse_additional_html_classes filter allows us to easily filter the additional classes added the HTML element. In the example below, a special class is added for the post id (of course there are many cases where there will be no post id). An array of custom class names is also added. Customize the classes/logic for adding classes to suit your needs, then return the array of class names.

add_filter( 'wpse_additional_html_classes', 'wpse_add_additional_html_classes', 10, 1 );
/**
 * Filters list of class names to be added to HTML element.
 *
 * @param array $classes
 * @return array
 */
function wpse_add_additional_html_classes( $classes ) {
    // Example of adding a post ID class.
    if ( is_singular() ) { 
        $post_id = get_the_ID();
        if ( $post_id ) {
            $post_id_class = "post-id-{$post_id}";
            if ( ! in_array( $post_id_class, $classes ) ) {
                $classes[] = $post_id_class;
            }
        }
    }

    // Add some more classes.
    $additional_classes = [
        'class-1',
        'class-2',
        'class-3',
    ];
    $classes = array_merge( $classes, $additional_classes );

    return $classes;
}
0

Since you're outside of the loop the cleanest solution is to simply create your own function like so

function wpse_268339_get_post_class() {
    global $post;
    if ( ! empty( $post->ID ) ) {
        return 'post-' . $post->ID;
    }
}

Then, in your header.php template just call the function as escape the output in a body class. This example assumes you already have a couple of classes on the <html> tag.

<html <?php language_attributes(); ?> class="no-js no-svg <?php echo esc_attr( wpse_268339_get_post_class() ); ?>">

This will only output a class name if you're on a Post / Page.

Alternatively, if all of the normal body classes don't harm anything, you can just use body_class() on the <html> tag as well. The function works anywhere.

<html <?php body_class(); ?>>
  • I'd prefer to not modify the actual PHP file. The theme does currently have classes (not unique ones) on the HTML element. I'd also prefer to not add the body classes to the HTML element, as I'm sure the theme doesn't expect that. – Garconis May 28 '17 at 1:33
0

Why not just add the class directly to the tag?

<html <?php language_attributes(); ?> class="page-id-<?php the_ID(); ?>">
  • I'm trying to avoid overriding theme files. – Garconis Oct 10 at 1:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.