Añadir una clase única a una etiqueta/elemento HTML
Sé que existe la función body_class en WordPress. ¿Pero hay alguna función (o forma) de añadir una clase al elemento HTML?
Mi objetivo es poder añadir una clase única (o ID) al elemento HTML de una página. Actualmente mi tema está añadiendo la clase page-id-XXXX al elemento body, pero necesito una forma de tener una clase o ID único en el elemento HTML real. Me conformaría con que también se añada page-id-XXXX al elemento HTML, aunque preferiría poder tener un campo personalizado que se añada a cada página, donde pueda escribir la clase/ID que luego se agregaría al elemento HTML.
Como mínimo, ¿hay alguna función que pueda usar para añadir una clase o ID al elemento HTML, similar a cómo funciona body_class?

WordPress no tiene un equivalente a body_class()
para la etiqueta HTML.
Aquí hay un enfoque que utiliza el almacenamiento en búfer de salida para capturar el resultado final del documento HTML que se está renderizando. El código de almacenamiento en búfer a continuación fue adaptado de soluciones publicadas aquí y aquí. Esto nos permite acceder al resultado final del documento HTML, que luego analizamos y editamos sin necesidad de modificar los archivos de plantilla del tema.
Primero, iniciamos el almacenamiento en búfer de salida y conectamos nuestra función shutdown
, que funciona iterando a través de todos los niveles de búfer abiertos, cerrándolos y capturando su salida. Luego activa el filtro wpse_final_output
, mostrando el contenido filtrado.
/**
* Iniciar el almacenamiento en búfer de salida
*/
add_action( 'wp', 'wpse_ob_start' );
function wpse_ob_start() {
// Salir inmediatamente si es el área de administración.
if ( is_admin() ) {
return;
}
// Salir inmediatamente si es un feed.
if ( is_feed() ) {
return;
}
// Iniciar el almacenamiento en búfer de salida.
ob_start();
add_action( 'shutdown', 'wpse_ob_clean', 0 );
}
/**
* Asegurarse de que el búfer esté limpio y luego activar el filtro wpse_final_output.
* Esto se ejecuta justo antes de la funcionalidad de cierre similar de WP.
*/
function wpse_ob_clean() {
$final = '';
// Necesitamos obtener el número de niveles de búfer en los que estamos, para poder
// iterar sobre cada uno, recopilando la salida de ese búfer en la salida final.
$levels = ob_get_level();
for ( $i = 0; $i < $levels; $i++ ) {
$final .= ob_get_clean();
}
// Aplicar cualquier filtro a la salida final
echo apply_filters( 'wpse_final_output', $final );
}
Aquí se analiza el HTML y se activa el filtro personalizado wpse_additional_html_classes
, que nos permite agregar nuestras clases adicionales en una función separada. Este código es un poco detallado, pero cubre varios casos extremos que he encontrado al usar DOMDocument
para analizar HTML.
add_filter( 'wpse_final_output', 'wpse_html_tag', 10, 1 );
/**
* Analizar la salida final del búfer. Activa wpse_additional_html_classes, lo que
* nos permite agregar clases al elemento HTML.
*/
function wpse_html_tag( $output ) {
// Lista filtrable de clases HTML.
$additional_html_classes = apply_filters( 'wpse_additional_html_classes', array() );
// Salir si no hay clases para agregar, ya que no necesitamos hacer nada.
if ( ! $additional_html_classes ) {
return $output;
}
// Crear una instancia de DOMDocument.
$dom = new \DOMDocument();
// Suprimir errores debido a HTML mal formado.
// Ver http://stackoverflow.com/a/17559716/3059883
$libxml_previous_state = libxml_use_internal_errors( true );
// Poblar $dom con el búfer, asegurándose de manejar UTF-8, de lo contrario
// ocurrirán problemas con caracteres UTF-8.
// Además, asegurarse de que las etiquetas doctype y HTML no se agreguen a nuestro fragmento HTML. http://stackoverflow.com/a/22490902/3059883
$dom->loadHTML( mb_convert_encoding( $output, 'HTML-ENTITIES', 'UTF-8' ), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
// Restaurar el estado anterior de libxml_use_internal_errors() ahora que hemos terminado.
// Nuevamente, ver http://stackoverflow.com/a/17559716/3059883
libxml_use_internal_errors( $libxml_previous_state );
// Crear una instancia de DOMXpath.
$xpath = new \DOMXpath( $dom );
// Obtener el primer elemento HTML.
$html = $xpath->query( "/descendant::html[1]" );
// Obtener las clases existentes para el elemento HTML.
$definedClasses = explode( ' ', $dom->documentElement->getAttribute( 'class' ) );
// Agrega nuestras clases adicionales a las clases existentes. Asegura que se use el espaciado adecuado de nombres de clase
// y que no se agreguen nombres de clase duplicados.
foreach ( $html as $html_tag ) {
$spacer = ' ';
// El espaciador se establecerá en una cadena vacía si no hay clases existentes.
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 = ' ';
}
}
// Guardar el HTML actualizado.
$output = $dom->saveHTML();
return $output;
}
El filtro wpse_additional_html_classes
nos permite filtrar fácilmente las clases adicionales agregadas al elemento HTML. En el ejemplo a continuación, se agrega una clase especial para el ID de la publicación (por supuesto, hay muchos casos en los que no habrá un ID de publicación). También se agrega una matriz de nombres de clases personalizados. Personaliza las clases/lógica para agregar clases según tus necesidades, luego devuelve la matriz de nombres de clases.
add_filter( 'wpse_additional_html_classes', 'wpse_add_additional_html_classes', 10, 1 );
/**
* Filtra la lista de nombres de clase para agregar al elemento HTML.
*
* @param array $classes
* @return array
*/
function wpse_add_additional_html_classes( $classes ) {
// Ejemplo de agregar una clase de ID de publicación.
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;
}
}
}
// Agregar algunas clases más.
$additional_classes = [
'class-1',
'class-2',
'class-3',
];
$classes = array_merge( $classes, $additional_classes );
return $classes;
}

Estoy considerando código temporal para modificar todas las etiquetas de encabezado para solucionar un error en WP v6, y una búsqueda me trajo aquí. Te agradezco amablemente por este código que haría exactamente lo que he estado considerando. Sin embargo, considerando el rendimiento, especialmente te agradezco por ayudarme a reconocer que esto es exactamente lo que no quiero hacer en un sitio de producción. Sí, esta es una gran solución, pero creo que solo como último recurso. Espero que este comentario ayude a alguien que esté tentado a simplemente copiar/pegar.

Esto funcionó perfectamente para mí
add_filter( 'language_attributes', 'add_no_js_class_to_html_tag', 10, 2 );
function add_no_js_class_to_html_tag( $output, $doctype ) {
// Verifica si el doctype es HTML
if ( 'html' !== $doctype ) {
return $output;
}
// Agrega la clase no-js al atributo de lenguaje
$output .= ' class="no-js"';
return $output;
}
No pude encontrar nada en el codex sobre esto. Crédito a https://gist.github.com/nickdavis/73d91d674b843b77a1cd0a21f9c0353a

¿Eso funcionó para añadir una clase personalizada? No hace nada en mi caso. Si lo configuro para que sea un atributo diferente en lugar de class, entonces funciona. Por ejemplo, esto funciona: function add_class_to_html_element( $output ) { $output .= ' attribute="value"'; return $output; } add_filter( 'language_attributes', 'add_class_to_html_element' );
... pero tan pronto como cambias attribute
por class
, no aparece. Parece que id
sí funcionaría.

Estoy usando ClassicPress 1.2, que se bifurcó desde WP 4.9, pero funciona en mi caso. <html lang="en-US" class="no-focus-outline"> es como se ve mi head ahora.

Como estás fuera del bucle, la solución más limpia es simplemente crear tu propia función como esta:
function wpse_268339_get_post_class() {
global $post;
if ( ! empty( $post->ID ) ) {
return 'post-' . $post->ID;
}
}
Luego, en tu plantilla header.php
simplemente llama a la función y escapa la salida en una clase del body. Este ejemplo asume que ya tienes un par de clases en la etiqueta <html>
.
<html <?php language_attributes(); ?> class="no-js no-svg <?php echo esc_attr( wpse_268339_get_post_class() ); ?>">
Esto solo mostrará un nombre de clase si estás en una Entrada / Página.
Alternativamente, si todas las clases normales del body no causan ningún problema, puedes usar body_class()
también en la etiqueta <html>
. La función funciona en cualquier lugar.
<html <?php body_class(); ?>>

Gracias a la ayuda de esta respuesta, encontré que esta solución funciona para agregar un id
. Aunque no estoy seguro sobre cómo agregarlo al atributo class
.
function add_id_to_html_element( $output ) {
global $post;
$output .= ' id="custom-id-' . $post->ID . '"';
return $output;
}
add_filter( 'language_attributes', 'add_id_to_html_element' );
