Добавление уникального класса к HTML-тегу/элементу
Я знаю, что в WordPress есть функция body_class. Но существует ли аналогичная функция (или способ) для добавления класса к HTML-элементу?
Моя цель - иметь возможность добавлять уникальный класс (или ID) к HTML-элементу страницы. В настоящее время моя тема добавляет класс page-id-XXXX к элементу body, но мне нужно, чтобы уникальный класс или ID был на самом HTML-элементе. Меня бы устроило, если бы класс page-id-XXXX также добавлялся к HTML-элементу, хотя я бы предпочел иметь возможность использовать произвольное поле на каждой странице, где можно ввести класс/ID, который затем будет добавлен к HTML-элементу.
Как минимум, существует ли функция, которую я могу использовать для добавления класса или ID к HTML-элементу, аналогично тому, как работает функция body_class?

В WordPress нет эквивалента функции body_class()
для HTML-тега.
Вот подход, использующий буферизацию вывода для захвата финального HTML-документа перед рендерингом. Код буферизации ниже адаптирован из решений, размещённых здесь и здесь. Это позволяет получить доступ к финальному выводу HTML-документа, который мы затем анализируем и редактируем, не изменяя файлы шаблонов темы.
Сначала мы запускаем буферизацию вывода и подключаем нашу функцию shutdown
, которая работает, перебирая все уровни открытых буферов, закрывает их и захватывает их вывод. Затем она запускает фильтр wpse_final_output
, выводя отфильтрованное содержимое.
/**
* Начинаем буферизацию вывода
*/
add_action( 'wp', 'wpse_ob_start' );
function wpse_ob_start() {
// Выходим, если это админка.
if ( is_admin() ) {
return;
}
// Выходим, если это фид.
if ( is_feed() ) {
return;
}
// Запускаем буферизацию вывода.
ob_start();
add_action( 'shutdown', 'wpse_ob_clean', 0 );
}
/**
* Очищаем буфер и запускаем фильтр wpse_final_output.
* Срабатывает прямо перед аналогичной функцией завершения в WordPress.
*/
function wpse_ob_clean() {
$final = '';
// Получаем количество уровней буферизации, чтобы пройтись по каждому
// и собрать вывод в финальный результат.
$levels = ob_get_level();
for ( $i = 0; $i < $levels; $i++ ) {
$final .= ob_get_clean();
}
// Применяем фильтры к финальному выводу
echo apply_filters( 'wpse_final_output', $final );
}
Здесь HTML анализируется, и срабатывает пользовательский фильтр wpse_additional_html_classes
, позволяющий добавить дополнительные классы в отдельной функции. Этот код немного многословен, но он охватывает несколько крайних случаев, с которыми я сталкивался при использовании DOMDocument
для разбора HTML.
add_filter( 'wpse_final_output', 'wpse_html_tag', 10, 1 );
/**
* Анализирует финальный вывод буфера. Запускает wpse_additional_html_classes,
* что позволяет добавить классы к HTML-элементу.
*/
function wpse_html_tag( $output ) {
// Фильтруемый список классов HTML.
$additional_html_classes = apply_filters( 'wpse_additional_html_classes', array() );
// Выходим, если нечего добавлять.
if ( ! $additional_html_classes ) {
return $output;
}
// Создаём экземпляр DOMDocument.
$dom = new \DOMDocument();
// Подавляем ошибки из-за некорректного HTML.
// См. http://stackoverflow.com/a/17559716/3059883
$libxml_previous_state = libxml_use_internal_errors( true );
// Заполняем $dom содержимым буфера, учитывая UTF-8,
// чтобы избежать проблем с UTF-8 символами.
// Также убеждаемся, что doctype и HTML теги не добавляются к нашему фрагменту. http://stackoverflow.com/a/22490902/3059883
$dom->loadHTML( mb_convert_encoding( $output, 'HTML-ENTITIES', 'UTF-8' ), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
// Восстанавливаем предыдущее состояние libxml_use_internal_errors().
// См. http://stackoverflow.com/a/17559716/3059883
libxml_use_internal_errors( $libxml_previous_state );
// Создаём экземпляр DOMXpath.
$xpath = new \DOMXpath( $dom );
// Получаем первый html элемент.
$html = $xpath->query( "/descendant::html[1]" );
// Получаем существующие классы для HTML элемента.
$definedClasses = explode( ' ', $dom->documentElement->getAttribute( 'class' ) );
// Добавляем дополнительные классы к существующим. Убеждаемся, что пробелы между именами классов
// используются правильно и дублирующиеся классы не добавляются.
foreach ( $html as $html_tag ) {
$spacer = ' ';
// Пробел будет пустым, если классов нет.
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 = ' ';
}
}
// Сохраняем обновлённый HTML.
$output = $dom->saveHTML();
return $output;
}
Фильтр wpse_additional_html_classes
позволяет легко добавлять классы к HTML-элементу. В примере ниже добавляется специальный класс для ID записи (конечно, во многих случаях ID записи может отсутствовать). Также добавляется массив пользовательских классов. Настройте классы и логику их добавления под свои нужды, затем верните массив имён классов.
add_filter( 'wpse_additional_html_classes', 'wpse_add_additional_html_classes', 10, 1 );
/**
* Фильтрует список имён классов для добавления к HTML элементу.
*
* @param array $classes
* @return array
*/
function wpse_add_additional_html_classes( $classes ) {
// Пример добавления класса с ID записи.
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;
}
}
}
// Добавляем ещё несколько классов.
$additional_classes = [
'class-1',
'class-2',
'class-3',
];
$classes = array_merge( $classes, $additional_classes );
return $classes;
}

Я рассматриваю временный код для изменения всех заголовков, чтобы обойти баг в WP версии 6 — поиск привел меня сюда. Большое спасибо за этот код, который делает именно то, что я рассматривал. Однако, учитывая производительность, я особенно благодарен за то, что вы помогли мне осознать, что это именно то, чего я не хочу делать на рабочем сайте. Да, это отличное решение, но, думаю, только как последнее средство. Надеюсь, этот комментарий поможет тем, кто будет искушен просто скопировать/вставить.

Этот вариант отлично сработал для меня
add_filter( 'language_attributes', 'add_no_js_class_to_html_tag', 10, 2 );
function add_no_js_class_to_html_tag( $output, $doctype ) {
if ( 'html' !== $doctype ) {
return $output;
}
$output .= ' class="no-js"';
return $output;
}
Я не смог найти ничего об этом в кодексе. Спасибо https://gist.github.com/nickdavis/73d91d674b843b77a1cd0a21f9c0353a

Это сработало для добавления пользовательского класса? У меня это ничего не делает. Если я установлю другой атрибут вместо class, тогда это работает. Например, это работает: function add_class_to_html_element( $output ) { $output .= ' attribute="value"'; return $output; } add_filter( 'language_attributes', 'add_class_to_html_element' );
... но как только вы меняете attribute
на class
, это не отображается. Похоже, что id
будет работать.

Я использую ClassicPress 1.2, который форкнулся от WP 4.9, но у меня это работает. <html lang="en-US" class="no-focus-outline"> - вот как сейчас выглядит мой head.

Поскольку вы находитесь вне цикла, самым чистым решением будет просто создать свою собственную функцию, например так:
function wpse_268339_get_post_class() {
global $post;
if ( ! empty( $post->ID ) ) {
return 'post-' . $post->ID;
}
}
Затем в вашем шаблоне header.php
просто вызовите функцию и экранируйте вывод в классе body. Этот пример предполагает, что у вас уже есть несколько классов в теге <html>
.
<html <?php language_attributes(); ?> class="no-js no-svg <?php echo esc_attr( wpse_268339_get_post_class() ); ?>">
Это будет выводить имя класса только если вы находитесь на странице записи/страницы.
Альтернативно, если все стандартные классы body не мешают, вы можете просто использовать body_class()
и в теге <html>
. Эта функция работает везде.
<html <?php body_class(); ?>>

Благодаря помощи этого ответа, я нашел решение, которое работает для добавления id
. Хотя я не уверен насчет добавления к атрибуту 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' );
