Добавление класса 'has_children' к родительскому li при модификации Walker_Nav_Menu
Я пишу кастомный класс walker для wp_nav_menu и хочу иметь возможность указывать, содержит ли элемент li подменю. Я хочу, чтобы моя разметка выглядела так:
<li class="has_children [другие-классы-wordpress]">
<a class="parent-link">Какой-то пункт</a>
<ul class="sub-menu">
Я знаю, как добавлять и удалять классы, но не могу найти способ определить, имеет ли текущий элемент дочерние пункты.
Есть идеи?
Заранее спасибо.

Метод start_el()
должен получать эту информацию в параметре $args
, но, похоже, WordPress заполняет это только если $args
является массивом, тогда как для пользовательских меню навигации это объект. Это описано в тикете Trac. Но нет проблем, вы можете заполнить это самостоятельно, если также переопределите метод display_element()
в вашем пользовательском Walker (потому что это самое простое место для доступа к массиву дочерних элементов):
class WPSE16818_Walker extends Walker_Nav_Menu
{
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output )
{
$id_field = $this->db_fields['id'];
if ( is_object( $args[0] ) {
$args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
}
return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
function start_el( &$output, $item, $depth, $args ) {
if ( $args->has_children ) {
// ...
}
}

Привет, Ян, можешь помочь с этим вопросом? Я пробовал твой код, но у меня не получилось заставить его работать. Можешь дать еще примеры кода?

Смотри полный пример реализации ниже на этой странице.

Обновление: Начиная с WordPress 3.7 (октябрь 2013 года), в темы меню добавлены CSS-классы, указывающие на дочерние пункты меню и страницы — больше нет необходимости использовать пользовательский walker, так как это обрабатывается в ядре WordPress.
CSS-классы называются menu-item-has-children
и page_item_has_children
.
Для полного решения для тех, кто торопится (спасибо предыдущему ответу Яна Фабри), смотрите полную реализацию ниже.
Вывод навигации в шаблоне вашей темы:
wp_nav_menu( array(
'theme_location' => 'navigation-primary',
'container' => false,
'container_class' => '',
'container_id' => '',
'menu_class' => '',
'menu_id' => '',
'walker' => new Selective_Walker(),
'depth' => 2
)
);
Затем добавьте следующее в файл functions.php
вашей темы:
class Selective_Walker extends Walker_Nav_Menu {
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
$id_field = $this->db_fields['id'];
if ( is_object( $args[0] ) ) {
$args[0]->has_children = !empty( $children_elements[$element->$id_field] );
}
return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
function start_el( &$output, $item, $depth, $args ) {
if ( $args->has_children ) {
$item->classes[] = 'has_children';
}
parent::start_el(&$output, $item, $depth, $args);
}
}
Результирующий HTML-вывод будет выглядеть примерно так:
<ul>
<li><a href="#">Главная</a></li>
<li class="has_children"><a href="#">О нас</a>
<ul class="sub-menu">
<li><a href="#">Наша миссия</a></li>
</ul>
</li>
<li><a href="#">Услуги</a></li>
<li class="has_children"><a href="#">Продукты</a>
<ul class="sub-menu">
<li><a href="#">Lorem Ipsum</a></li>
<li><a href="#">Lorem Ipsum</a></li>
</ul>
</li>
<li><a href="#">Контакты</a></li>
</ul>
Для получения дополнительной информации об использовании класса walker в WordPress, смотрите Understanding the Walker Class.
Удачи!

Эта функция именно то, что вам нужно. Она также демонстрирует эффективный способ модификации элементов навигационного меню. Кроме того, вы можете расширить её для более сложных функций (например, в дочерней теме) с помощью фильтра элементов:
/**
* Классы для навигации с названием "Topnav" в области меню "top".
* Примеры модификации текущего элемента навигационного меню
*
* @param (object) $items
* @param (object) $menu
* @param (object) $args
*/
function wpse16818_nav_menu_items( $items, $menu, $args )
{
# >>>> начало редактирования
// примеры возможных целей
$target['name'] = 'Topnav';
// Целевые элементы меню
$target['items'] = array( (int) 6 );
# <<<< конец редактирования
// фильтр для дочерних тем: "config_nav_menu_topnav"
$target = apply_filters( 'config_nav_menu_'.strtolower( $target['name'] ), $target );
// Прерываем, если это не целевое меню
if ( $menu->name !== $target['name'] )
return;
foreach ( $items as $item )
{
// Проверяем содержимое $item
echo '<pre>'; print_r($item); echo '</pre>';
// Первый практический пример:
$item->classes = 'span-4';
// Второй практический пример:
// Добавляем этот класс, если находимся в одном из целевых элементов
if ( in_array( (int) $item->menu_order, $target['items'] ) )
$item->classes .= ' last';
}
return $items;
}
add_filter( 'wp_get_nav_menu_items', 'wpse16818_nav_menu_items', 10, 3 );
И да, в подавляющем большинстве случаев нет необходимости в пользовательском walker.

Если вам нужно создать выпадающее меню, это можно сделать только с помощью CSS.
Создайте пользовательское меню в WordPress с дочерними элементами, WordPress автоматически присваивает класс .sub-menu дочернему списку ul. Попробуйте этот CSS:
nav li {position:relative;}
.sub-menu {display:none; position:absolute; width:300px;}
nav ul li:hover ul {display:block;}
Вы можете добавить немного jQuery для дополнительных эффектов, но приведённого кода достаточно для рабочего выпадающего меню.
