WordPress как на ладони
rgbcode is looking for WordPress developers. Очень Удобный и Быстрый Хостинг для сайтов на WordPress. Пользуюсь сам и вам рекомендую!

wp_nav_menu()WP 3.0.0

Выводит произвольное меню, созданное в админ-панели: "внешний вид > меню" (Appearance > Menus).

Какое именно меню навигации выводить (их может быть несколько), указывается в параметре theme_location.

Если не указан параметр theme_location, то меню для вывода будет подбираться в следующем порядке:

  1. Меню, подходящее по ID, слагу или описанию переданному в параметре 'menu' и если в этом меню есть хоть одна ссылка (один элемент);

  2. иначе, первое не пустое меню;

  3. или, выведет значение возвращаемое функцией указанной в параметре "fallback_cb" (по умолчанию там указано функция wp_page_menu);

  4. если ничего не подошло, функция ничего и не выведет.

Чтобы тема поддерживала меню, нужно включить эту возможность с помощью:
add_theme_support( 'menus' );

Или можно зарегистрировать место для меню, с помощью register_nav_menu(), тогда поддержка меню темой включиться автоматически.

Дополнительно про включение и добавление меню читайте в отдельной статье.

Фильтры для изменения элемента меню

  • wp_nav_menu_args - фильтрует список параметров wp_nav_menu().

  • nav_menu_item_args - фильтрует параметры отдельного элемента меню.

  • nav_menu_css_class - фильтрует css классы (class атрибут) отдельного <li> элемента меню.

  • nav_menu_item_id - фильтрует ID атрибут отдельного <li> элемента меню.

  • nav_menu_link_attributes - фильтрует атрибуты <a> элемента меню: title, target, rel, href.

  • nav_menu_item_title - фильтрует текст анкора ссылки элемента меню.

  • walker_nav_menu_start_el - фильтрует HTML код начала отдельного элемента меню. Начало означает незакрытый li тег: <li><a></a>

  • nav_menu_submenu_css_class - позволяет изменить CSS классы, добавленные к вложенным ul элементам (спискам меню). По умолчанию добавляется класс sub-menu.

Возвращает

null|Строку|false. Функция выводит на экран html код меню.

Шаблон использования

wp_nav_menu( [
	'theme_location'  => '',
	'menu'            => '',
	'container'       => 'div',
	'container_class' => '',
	'container_id'    => '',
	'menu_class'      => 'menu',
	'menu_id'         => '',
	'echo'            => true,
	'fallback_cb'     => 'wp_page_menu',
	'before'          => '',
	'after'           => '',
	'link_before'     => '',
	'link_after'      => '',
	'items_wrap'      => '<ul id="%1$s" class="%2$s">%3$s</ul>',
	'depth'           => 0,
	'walker'          => '',
] );

Использование

<?php wp_nav_menu( $args ); ?>
$args(массив)
Массив параметров выводимого меню.
По умолчанию: array() (предустановки)

Аргументы параметра $args

theme_location(строка)
Идентификатор расположение меню в шаблоне. Идентификатор, указывается при регистрации меню функцией register_nav_menu().
По умолчанию: ''
Меню которое нужно вывести. Можно указать: id, slug или название меню.
По умолчанию: ''
container(строка/false)
Чем оборачивать ul тег. Может быть: div или nav.
Если не нужно оборачивать ничем, то пишем false: container => false.
По умолчанию: div
container_class(строка)
Значение атрибута class="" у контейнера меню.
По умолчанию: menu-{menu slug}-container
container_id(строка)
Значение атрибута id="" у контейнера меню.
По умолчанию: ''
container_aria_label(строка) (WP 5.5)
Значение атрибута aria-label="" у контейнера меню.
По умолчанию: ''
Значение атрибута class у тега ul.
По умолчанию: menu
Значение атрибута id у тега ul.
По умолчанию: menu-{menu slug}
items_wrap(строка)
Шаблон обёртки для элементов меню. Шаблон обязательно должен иметь плейсхолдер %3$s, остальное опционально.
По умолчанию: '<ul id="%1$s" class="%2$s">%3$s</ul>'
fallback_cb(строка)
Функция для обработки вывода, если никакое меню не найдено.
Передает все аргументы $args указанной тут функции.
Установите пустую строку '' или '__return_empty_string', чтобы ничего не выводилось, если меню нет.
По умолчанию: wp_page_menu
before(строка)
Текст перед тегом <a> в меню.
По умолчанию: ''
after(строка)
Текст после каждого тега </a> в меню.
По умолчанию: ''
Текст перед анкором каждой ссылки в меню.
По умолчанию: ''
Текст после анкора каждой ссылки в меню.
По умолчанию: ''
depth(число)
До какого уровня вложенности нужно показывать ссылки (элементы меню). 0 - все уровни.
По умолчанию: 0
item_spacing(строка) (WP 4.7)
Оставлять или нет переносы строк в HTML коде меню. Может быть: preserve или discard
По умолчанию: 'preserve'
echo(true/false)
Выводить на экран или возвратить для обработки.
По умолчанию: true
walker(объект)

Класса, который будет использоваться для построения меню. Нужно указывать экземпляр объекта, а не строку, например new My_Menu_Walker().

Как использовать смотрите в этом примере.
По умолчанию: Walker_Nav_Menu()

Примеры

10

#1 CSS класс для родительских элементов меню

Если нужно добавить CSS класс для элементов меню, у которых есть дочерние (сложенные списки ссылок), то делаем так:

add_filter( 'wp_nav_menu_objects', 'css_for_nav_parrent' );
function css_for_nav_parrent( $items ){

	foreach( $items as $item ){

		if( __nav_hasSub( $item->ID, $items ) ){
			// все элементы поля "classes" меню, будут совмещены и выведены в атрибут class HTML тега <li>
			$item->classes[] = 'menu-parent-item';
		}

	}

	return $items;
}

function __nav_hasSub( $item_id, $items ){

	foreach( $items as $item ){

		if( $item->menu_item_parent && $item->menu_item_parent == $item_id )
			return true;
	}

	return false;
}
6

#2 Первое не пустое меню, с настройками вывода по умолчанию:

<?php wp_nav_menu(); ?>
5

#3 Удалим ul обертку

Этот пример удалит обертку тега ul у меню:

<?php wp_nav_menu( [ 'items_wrap' => '%3$s' ] ); ?>
2

#4 Использование своей функции для построения меню

Пример расширения класса Walker_Nav_Menu, для создания своего произвольного HTML кода который выводится функцией wp_nav_menu(). Наш HTML код будет написан специально для нашей темы.

Ниже код своего произвольного класса, который строит меню. Он добавляет глубину меню и четные/нечетные CSS классы к элементам меню (обоим ul и li):

Чтобы не изобретать велосипед, копируем код класса Walker_Nav_Menu{} и просто изменяем его как нам нужно.

// свой класс построения меню:
class My_Walker_Nav_Menu extends Walker_Nav_Menu {

	// add classes to ul sub-menus
	function start_lvl( &$output, $depth = 0, $args = NULL ) {
		// depth dependent classes
		$indent = ( $depth > 0  ? str_repeat( "\t", $depth ) : '' ); // code indent
		$display_depth = ( $depth + 1); // because it counts the first submenu as 0
		$classes = array(
			'sub-menu',
			( $display_depth % 2  ? 'menu-odd' : 'menu-even' ),
			( $display_depth >=2 ? 'sub-sub-menu' : '' ),
			'menu-depth-' . $display_depth
		);
		$class_names = implode( ' ', $classes );

		// build html
		$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
	}

	// add main/sub classes to li's and links
	function start_el( &$output, $data_object, $depth = 0, $args = null, $current_object_id = 0 ) {
		global $wp_query;

		// Restores the more descriptive, specific name for use within this method.
		$item = $data_object;

		$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent

		// depth dependent classes
		$depth_classes = array(
			( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
			( $depth >=2 ? 'sub-sub-menu-item' : '' ),
			( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
			'menu-item-depth-' . $depth
		);
		$depth_class_names = esc_attr( implode( ' ', $depth_classes ) );

		// passed classes
		$classes = empty( $item->classes ) ? array() : (array) $item->classes;
		$class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );

		// build html
		$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';

		// link attributes
		$attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
		$attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
		$attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
		$attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';
		$attributes .= ' class="menu-link ' . ( $depth > 0 ? 'sub-menu-link' : 'main-menu-link' ) . '"';

		$item_output = sprintf( '%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s',
			$args->before,
			$attributes,
			$args->link_before,
			apply_filters( 'the_title', $item->title, $item->ID ),
			$args->link_after,
			$args->after
		);

		// build html
		$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
	}

}

Теперь, когда класс готов, используем его в функции wp_nav_menu(). Для этого нужно указать экземпляр нашего класса в параметре walker.

function my_nav_menu( $args ) {

	$args = array_merge( [
		'container'       => 'div',
		'container_id'    => 'top-navigation-primary',
		'container_class' => 'top-navigation',
		'menu_class'      => 'menu main-menu menu-depth-0 menu-even',
		'echo'            => false,
		'items_wrap'      => '<ul id="%1$s" class="%2$s">%3$s</ul>',
		'depth'           => 10,
		'walker'          => new My_Walker_Nav_Menu()
	], $args );

	echo wp_nav_menu( $args );
}

Теперь там, где нужно выводить меню используем нашу функцию:

my_nav_menu( [ 'theme_location'  => 'navigation_menu_primary' ] );

1

#5 Добавим CSS классы ко всем меню

Используя хук, мы можем добавить свои CSS классы, если соблюдается нужное нам условие.

Добавим CSS класс, если это пост и название элемента меню равно "blog":

add_filter( 'nav_menu_css_class', 'special_nav_class', 10, 2 );
function special_nav_class($classes, $item){
	if( is_single() && $item->title == "Blog" ){
		$classes[] = "special-class";
	}

	return $classes;
}
1

#6 Вывод только подпункта меню

Допустим, есть первый уровень и у каждого из элементов первого уровня, есть свое подменю. Нам нужно вывести такое подменю у пункта с классом menu-item-135:

## Вырезаем все LI нужного submenu и выводим их в своем UL блоке
$menu = wp_nav_menu( array(
	'theme_location'  => 'header_menu',
	'container'       => '',
	'echo'            => 0,
) );

$regex_part = preg_quote('menu-item-135');

// выведем подменю пункта "gotovye-resheniya"
preg_match('~'. $regex_part .'.*sub-menu[^>]+>(.*?)</ul>~s', $menu, $mm );

if( ! empty( $mm[1] ) )
	echo "<ul>$mm[1]</ul>";

Не очень оптимальный, но рабочий пример. Иногда может пригодится, для малопосещаемых сайтов где нужно быстро получить результат.

0

#7 Выведем меню с названием "Навигация по сайту":

<?php wp_nav_menu( [ 'menu' => 'Навигация по сайту' ] ); ?>
0

#8 Меню из страниц. Пример из темы: Twenty Ten.

Если параметры вывода не указаны и меню не найдено, то будет построено меню из страниц, функцией wp_page_menu().

В этом примере будет выведено меню, прикрепленное к области меню 'primary':

<div id="access" role="navigation">
	<?php wp_nav_menu( [
		'container_class' => 'menu-header',
		'theme_location'  => 'primary'
	] ); ?>
</div>
0

#9 Использование фильтра wp_nav_menu_args для установки параметров по умолчанию для всех меню

Чтобы удалить контейнер у всех навигационных меню сразу, используем следующих код в файле темы functions.php. Используем хук wp_nav_menu_args:

add_filter( 'wp_nav_menu_args', 'my_wp_nav_menu_args' );
function my_wp_nav_menu_args( $args = '' ){
	$args['container'] = false;
	return $args;
}
0

#10 Удалим контейнер, только у одного, выводимого меню

<?php wp_nav_menu( [ 'container' => '' ] ); ?>
0

#11 Добавим слово в начало меню

Этот пример показывает, как добавить слово в начало списка меню, в виде такого же элемента меню, только не ссылки.

Добавим в начало меню, слово "Список", также укажем атрибут id созданному тегу li:

<?php wp_nav_menu( [
	'theme_location' => 'primary',
	'items_wrap'     => '<ul><li id="item-id">Список: </li>%3$s</ul>'
] ); ?>
0

#12 Отдельные меню для авторизованных пользователей

Если нужно показывать разные меню, авторизованным и неавторизованным пользователям, то используем условный тег is_user_logged_in():

if ( is_user_logged_in() ) {
	 wp_nav_menu( array( 'theme_location' => 'logged-in-menu' ) );
}
else {
	 wp_nav_menu( array( 'theme_location' => 'logged-out-menu' ) );
}

В админке нужно создать 2 разных меню и прикрепить их к соответствующим локациям (областям).

0

#13 Добавление класса к отдельным элементам меню

Появился специальных хук для этого: nav_menu_css_class. Классы можно добавлять или удалять через него. Для примера давайте добавим класс my__class ко всем элементам меню:

add_filter( 'nav_menu_css_class', 'add_my_class_to_nav_menu', 10, 2 );
function add_my_class_to_nav_menu( $classes, $item ){
	/* $classes содержит
	Array(
		[1] => menu-item
		[2] => menu-item-type-post_type
		[3] => menu-item-object-page
		[4] => menu-item-284
	)
	*/
	$classes[] = 'my__class';

	return $classes;
}
0

#14 Выводить меню, только если оно существует

По умолчанию, если меню нет, то вместо него будут выведены страницы сайта. Но если нужно выводить меню, только в том случае когда оно создано в админ-панели, укажите параметр fallback_cb как '__return_empty_string':

wp_nav_menu( array(
	'theme_location' => 'primary-menu',
	'fallback_cb' => '__return_empty_string'
) );
0

#15 Кэширование меню (wp_nav_menu) в кэш объектов

Код ниже показывает как можно кэшировать весь код меню в объектный кэш. Кэш будет сбрасываться при обновлении меню. Кэширование будет работать для всех меню.

GitHub

<?php

Pj_Cached_Nav_Menus::load();

/**
 * Caches calls to wp_nav_menu().
 */
class Pj_Cached_Nav_Menus {
	public static $ttl = 3600; // use 0 to cache forever (until nav menu update)
	public static $cache_menus = array();

	public static function load() {
		add_filter( 'pre_wp_nav_menu', array( __CLASS__, 'pre_wp_nav_menu' ), 10, 2 );
		add_filter( 'wp_nav_menu', array( __CLASS__, 'maybe_cache_nav_menu' ), 10, 2 );
		add_action( 'wp_update_nav_menu', array( __CLASS__, 'clear_caches' ) );
	}

	private static function _cache_key( $args ) {
		$_args = (array) $args;
		unset( $_args['menu'] );
		return 'pj-cached-nav-menu:' . md5( json_encode( $_args ) );
	}

	private static function _timestamp() {
		static $timestamp;
		if ( ! isset( $timestamp ) )
			$timestamp = get_option( 'pj-cached-nav-menus-timestamp', 0 );

		return $timestamp;
	}

	public static function pre_wp_nav_menu( $output, $args ) {
		if ( ! empty( $args->menu ) )
			return $output;

		$cache_key = self::_cache_key( $args );
		self::$cache_menus[] = $cache_key;

		$cache = get_transient( $cache_key );
		if ( is_array( $cache ) && $cache['timestamp'] >= self::_timestamp() ) {
			$output = $cache['html'] . '<!-- pj-cached-nav-menu -->';
		}

		return $output;
	}

	public static function maybe_cache_nav_menu( $html, $args ) {
		$cache_key = self::_cache_key( $args );

		if ( ! in_array( $cache_key, self::$cache_menus ) )
			return $html;

		$cache = array(
			'html' => $html,
			'timestamp' => time(),
		);

		set_transient( $cache_key, $cache, self::$ttl );
		return $html;
	}

	public static function clear_caches() {
		update_option( 'pj-cached-nav-menus-timestamp', time() );
	}
}

Оригинальный код: https://github.com/pressjitsu/cached-nav-menus

Композер пакет: https://github.com/inpsyde/menu-cache Статья по нему: https://www.kobzarev.com/wordpress/cache-nav-menus/

Другой аналогичный пример: https://www.bjornjohansen.com/wordpress-menu-cache

CSS классы элементов меню

Добавляемые классы смотрите в функции _wp_menu_item_classes_by_context().

Следующие CSS классы добавляются к элементам меню (разделение по условиям на каких страницах находится пользователь):

Для всех элементов на всех страницах

  • .menu-item — ко всем элементам меню;

  • .menu-item-object-{object} — ко всем элементам. {object} замениться на название типа записи или таксономии:

    • .menu-item-object-category — для категорий.
    • .menu-item-object-tag — для меток.
    • .menu-item-object-page — для постоянных страниц.
    • .menu-item-object-custom ‒ для произвольного элемента меню.
  • .menu-item-type-{type} — ко всем элементам меню. {type} замениться на тип ссылки (запись или таксономия):
    • .menu-item-type-post_type — постоянная страница, произвольный тип записи.
    • .menu-item-type-taxonomy — категория, метка или произвольная таксономия.
    • .menu-item-type-custom ‒ для произвольного элемента меню.

На текущей странице

  • .current-menu-item — если ссылка в меню совпадает с адресом просматриваемой страницы. Текущая страница.

Для родительских элементов у просматриваемой страницы

  • .current-menu-parent
  • .current-{taxonomy}-ancestor
  • .current-{post_type}-ancestor

Для элементов как-то связанных с просматриваемой страницей

  • .current-menu-ancestor
  • .current-{object}-ancestor
  • .current-{type}-ancestor

Для элементов связанных с главной страницей сайта

  • .menu-item-home

Совместимость с функцией wp_page_menu()

  • .page_item
  • .page-item-{$menu_item->object_id}
  • .current_page_item
  • .current_page_parent
  • .current_page_ancestor

Остальное

  • .menu-item-has-children ‒ Если у элемента есть дочерние элементы.
  • ul.sub-menu ‒ У дочернего элемента ul.
  • .menu-item-privacy-policy ‒ пункт меню "Страница политики конфиденциальности".
  • .menu-item-home ‒ пункт меню "Главная страница".

Объект $item

Параметры $item

В примерах часто используется элемент меню $item. Ниже показаны почти все параметры этого элемента:

Поле Описание
ID ID элемента меню
menu_item_parent ID родительского элемента меню
classes массив классов элемента меню
post_date дата добавления
post_modified дата последнего изменения
post_author ID пользователя, добавившего этот элемент меню
title заголовок элемента меню
url ссылка элемента меню
attr_title title атрибут ссылки
xfn rel атрибут ссылки
target target атрибут ссылки
current равен 1, если это текущий элемент
current_item_ancestor 1, если текущий элемент - это вложенный элемент
current_item_parent 1 если текущим элемент - это родительский элемент
menu_order порядковый номер в меню
object_id ID объекта меню. Записи, термина и т.д.
type тип объекта меню (такса, запись)
object название таксы, типа записи: page, category, post_tag ...
type_label локализованное название типа: Рубрика, Страница
post_parent ID родительской записи
post_title заголовок записи
post_name ярлык записи
Пример объекта $item
WP_Post Object (
	[ID]                    => 10
	[post_author]           => 5
	[post_date]             => 2019-02-11 13:33:39
	[post_date_gmt]         => 2019-02-11 13:33:39
	[post_content]          =>
	[post_title]            => New
	[post_excerpt]          =>
	[post_status]           => publish
	[comment_status]        => closed
	[ping_status]           => closed
	[post_password]         =>
	[post_name]             => new
	[to_ping]               =>
	[pinged]                =>
	[post_modified]         => 2019-02-11 23:10:19
	[post_modified_gmt]     => 2019-02-11 23:10:19
	[post_content_filtered] =>
	[post_parent]           => 0
	[guid]                  => http://dh5.com/?p=10
	[menu_order]            => 1
	[post_type]             => nav_menu_item
	[post_mime_type]        =>
	[comment_count]         => 0
	[filter]                => raw
	[db_id]                 => 10
	[menu_item_parent]      => 0
	[object_id]             => 10
	[object]                => custom
	[type]                  => custom
	[type_label]            => Custom Link
	[title]                 => New
	[url]                   => #
	[target]                =>
	[attr_title]            =>
	[description]           =>
	[classes]               => Array
		[0] => extra-sub-menu
		[1] => menu-item
		[2] => menu-item-type-custom
		[3] => menu-item-object-custom
	[xfn]                   =>
	[current]               =>
	[current_item_ancestor] =>
	[current_item_parent]   =>
)

Пример использования параметра walker

В walker можно указать объект, который будет строить меню. В этом объекте можно описать HTML код получаемого меню.

Если нужно создать меню для нестандартной верстки, то иногда проще переделать этот объект, чем переделывать верстку.

В качестве примера walker объекта, возьмем класс Walker_Nav_Menu{}, который используется по умолчанию. В нём нас интересует только один метод start_el(). Именно он отвечает за HTML каждого элемента. Как правило, достаточно изменить только его. Для этого нужно создать свой класс, который будет расширять класс Walker_Nav_Menu и указать его в параметре walker при вызове меню.

Смотрим на примере. Взят код метода start_el() без изменений. Используем в качестве шаблона:

class My_Walker_Nav_Menu extends Walker_Nav_Menu {

	/**
	 * Starts the element output.
	 *
	 * @since 3.0.0
	 * @since 4.4.0 The {@see 'nav_menu_item_args'} filter was added.
	 *
	 * @see Walker::start_el()
	 *
	 * @param string   $output Passed by reference. Used to append additional content.
	 * @param WP_Post  $item   Menu item data object.
	 * @param int      $depth  Depth of menu item. Used for padding.
	 * @param stdClass $args   An object of wp_nav_menu() arguments.
	 * @param int      $id     Current item ID.
	 */
	public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
		if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
			$t = '';
			$n = '';
		} else {
			$t = "\t";
			$n = "\n";
		}
		$indent = ( $depth ) ? str_repeat( $t, $depth ) : '';

		$classes = empty( $item->classes ) ? array() : (array) $item->classes;
		$classes[] = 'menu-item-' . $item->ID;

		$args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );

		$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
		$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

		$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args, $depth );
		$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';

		// создаем HTML код элемента меню
		$output .= $indent . '<li' . $id . $class_names .'>';

		$atts = array();
		$atts['title']  = ! empty( $item->attr_title ) ? $item->attr_title : '';
		$atts['target'] = ! empty( $item->target )     ? $item->target     : '';
		$atts['rel']    = ! empty( $item->xfn )        ? $item->xfn        : '';
		$atts['href']   = ! empty( $item->url )        ? $item->url        : '';

		$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );

		$attributes = '';
		foreach ( $atts as $attr => $value ) {
			if ( ! empty( $value ) ) {
				$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
				$attributes .= ' ' . $attr . '="' . $value . '"';
			}
		}

		$title = apply_filters( 'the_title', $item->title, $item->ID );
		$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );

		$item_output = $args->before;
		$item_output .= '<a'. $attributes .'>';
		$item_output .= $args->link_before . $title . $args->link_after;
		$item_output .= '</a>';
		$item_output .= $args->after;

		$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
	}

}

Теперь, при вызове меню указываем свой walker:

wp_nav_menu( array(
	'theme_location' => 'head_menu',
	'walker'         => new My_Walker_Nav_Menu(),
) );

Готово, теперь каждый элемент меню будет строиться по нужной нам HTML схеме.

БЭМ меню с помощью фильтров

Будет формироваться вёрстка по методологии БЭМ:

<ul class="menu menu--main menu--horizontal">
	<li class="menu-node menu-node--main_lvl_1 menu-node--active">
		<a href="#" class="menu-link menu-link--active">Пункт 1</a>
		<ul class="menu menu--dropdown menu--vertical">
			<li class="menu-node menu-node--main_lvl_2">
				<a href="#" class="menu-link">Подпункт 1.1</a>
			</li>
			<li class="menu-node menu-node--main_lvl_2">
				<a href="#" class="menu-link">Подпункт 1.2</a>
			</li>
		</ul>
	</li>
	<li class="menu-node menu-node--main_lvl_1">
		<a href="#" class="menu-link">Пункт 2</a>
	</li>
	<li class="menu-node menu-node--main_lvl_1">
		<a href="#" class="menu-link">Пункт 3</a>
	</li>
</ul>

Файл index.php или другой для вывода меню

<?php
wp_nav_menu( [
	'theme_location' => 'header-menu',
] );

Файл functions.php

<?php
add_action( 'after_setup_theme', function () {
	register_nav_menus( [
		'header-menu' => 'Верхняя область',
		'footer-menu' => 'Нижняя область',
	] );
} );

// Изменяет основные параметры меню
add_filter( 'wp_nav_menu_args', 'filter_wp_menu_args' );

// Изменяем атрибут id у тега li
add_filter( 'nav_menu_item_id', 'filter_menu_item_css_id', 10, 4 );

// Изменяем атрибут class у тега li
add_filter( 'nav_menu_css_class', 'filter_nav_menu_css_classes', 10, 4 );

// Изменяет класс у вложенного ul
add_filter( 'nav_menu_submenu_css_class', 'filter_nav_menu_submenu_css_class', 10, 3 );

// Добавляем классы ссылкам
add_filter( 'nav_menu_link_attributes', 'filter_nav_menu_link_attributes', 10, 4 );

function filter_wp_menu_args( $args ) {
	if ( $args['theme_location'] === 'header-menu' ) {
		$args['container']  = false;
		$args['items_wrap'] = '<ul class="%2$s">%3$s</ul>';
		$args['menu_class'] = 'menu menu--main menu-horizontal';
	}
	return $args;
}

function filter_menu_item_css_id( $menu_id, $item, $args, $depth ) {
	return $args->theme_location === 'header-menu' ? '' : $menu_id;
}

function filter_nav_menu_css_classes( $classes, $item, $args, $depth ) {
	if ( $args->theme_location === 'header-menu' ) {
		$classes = [
			'menu-node',
			'menu-node--main_lvl_' . ( $depth + 1 )
		];
		if ( $item->current ) {
			$classes[] = 'menu-node--active';
		}
	}
	return $classes;
}

function filter_nav_menu_submenu_css_class( $classes, $args, $depth ) {
	if ( $args->theme_location === 'header-menu' ) {
		$classes = [
			'menu',
			'menu--dropdown',
			'menu--vertical'
		];
	}
	return $classes;
}

function filter_nav_menu_link_attributes( $atts, $item, $args, $depth ) {
	if ( $args->theme_location === 'header-menu' ) {
		$atts['class'] = 'menu-link';
		if ( $item->current ) {
			$atts['class'] .= ' menu-link--active';
		}
	}
	return $atts;
}

Заказывайте очень дешевые лайки на публичную страницу в Facebook на данном сервисе и получайте возможность выбрать не только приятную цену, но и персональные условия для приобретения ресурса. Так, например, Вам будет доступен выбор скоростного режима поступления и качества подписываемых страниц. Кроме того, сервис предоставляет гарантии своим клиентам.

Список изменений

С версии 3.0.0 Введена.
С версии 4.7.0 Added the item_spacing argument.
С версии 5.5.0 Added the container_aria_label argument.

Код wp_nav_menu() WP 6.4.3

function wp_nav_menu( $args = array() ) {
	static $menu_id_slugs = array();

	$defaults = array(
		'menu'                 => '',
		'container'            => 'div',
		'container_class'      => '',
		'container_id'         => '',
		'container_aria_label' => '',
		'menu_class'           => 'menu',
		'menu_id'              => '',
		'echo'                 => true,
		'fallback_cb'          => 'wp_page_menu',
		'before'               => '',
		'after'                => '',
		'link_before'          => '',
		'link_after'           => '',
		'items_wrap'           => '<ul id="%1$s" class="%2$s">%3$s</ul>',
		'item_spacing'         => 'preserve',
		'depth'                => 0,
		'walker'               => '',
		'theme_location'       => '',
	);

	$args = wp_parse_args( $args, $defaults );

	if ( ! in_array( $args['item_spacing'], array( 'preserve', 'discard' ), true ) ) {
		// Invalid value, fall back to default.
		$args['item_spacing'] = $defaults['item_spacing'];
	}

	/**
	 * Filters the arguments used to display a navigation menu.
	 *
	 * @since 3.0.0
	 *
	 * @see wp_nav_menu()
	 *
	 * @param array $args Array of wp_nav_menu() arguments.
	 */
	$args = apply_filters( 'wp_nav_menu_args', $args );
	$args = (object) $args;

	/**
	 * Filters whether to short-circuit the wp_nav_menu() output.
	 *
	 * Returning a non-null value from the filter will short-circuit wp_nav_menu(),
	 * echoing that value if $args->echo is true, returning that value otherwise.
	 *
	 * @since 3.9.0
	 *
	 * @see wp_nav_menu()
	 *
	 * @param string|null $output Nav menu output to short-circuit with. Default null.
	 * @param stdClass    $args   An object containing wp_nav_menu() arguments.
	 */
	$nav_menu = apply_filters( 'pre_wp_nav_menu', null, $args );

	if ( null !== $nav_menu ) {
		if ( $args->echo ) {
			echo $nav_menu;
			return;
		}

		return $nav_menu;
	}

	// Get the nav menu based on the requested menu.
	$menu = wp_get_nav_menu_object( $args->menu );

	// Get the nav menu based on the theme_location.
	$locations = get_nav_menu_locations();
	if ( ! $menu && $args->theme_location && $locations && isset( $locations[ $args->theme_location ] ) ) {
		$menu = wp_get_nav_menu_object( $locations[ $args->theme_location ] );
	}

	// Get the first menu that has items if we still can't find a menu.
	if ( ! $menu && ! $args->theme_location ) {
		$menus = wp_get_nav_menus();
		foreach ( $menus as $menu_maybe ) {
			$menu_items = wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) );
			if ( $menu_items ) {
				$menu = $menu_maybe;
				break;
			}
		}
	}

	if ( empty( $args->menu ) ) {
		$args->menu = $menu;
	}

	// If the menu exists, get its items.
	if ( $menu && ! is_wp_error( $menu ) && ! isset( $menu_items ) ) {
		$menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) );
	}

	/*
	 * If no menu was found:
	 *  - Fall back (if one was specified), or bail.
	 *
	 * If no menu items were found:
	 *  - Fall back, but only if no theme location was specified.
	 *  - Otherwise, bail.
	 */
	if ( ( ! $menu || is_wp_error( $menu ) || ( isset( $menu_items ) && empty( $menu_items ) && ! $args->theme_location ) )
		&& isset( $args->fallback_cb ) && $args->fallback_cb && is_callable( $args->fallback_cb ) ) {
			return call_user_func( $args->fallback_cb, (array) $args );
	}

	if ( ! $menu || is_wp_error( $menu ) ) {
		return false;
	}

	$nav_menu = '';
	$items    = '';

	$show_container = false;
	if ( $args->container ) {
		/**
		 * Filters the list of HTML tags that are valid for use as menu containers.
		 *
		 * @since 3.0.0
		 *
		 * @param string[] $tags The acceptable HTML tags for use as menu containers.
		 *                       Default is array containing 'div' and 'nav'.
		 */
		$allowed_tags = apply_filters( 'wp_nav_menu_container_allowedtags', array( 'div', 'nav' ) );

		if ( is_string( $args->container ) && in_array( $args->container, $allowed_tags, true ) ) {
			$show_container = true;
			$class          = $args->container_class ? ' class="' . esc_attr( $args->container_class ) . '"' : ' class="menu-' . $menu->slug . '-container"';
			$id             = $args->container_id ? ' id="' . esc_attr( $args->container_id ) . '"' : '';
			$aria_label     = ( 'nav' === $args->container && $args->container_aria_label ) ? ' aria-label="' . esc_attr( $args->container_aria_label ) . '"' : '';
			$nav_menu      .= '<' . $args->container . $id . $class . $aria_label . '>';
		}
	}

	// Set up the $menu_item variables.
	_wp_menu_item_classes_by_context( $menu_items );

	$sorted_menu_items        = array();
	$menu_items_with_children = array();
	foreach ( (array) $menu_items as $menu_item ) {
		/*
		 * Fix invalid `menu_item_parent`. See: https://core.trac.wordpress.org/ticket/56926.
		 * Compare as strings. Plugins may change the ID to a string.
		 */
		if ( (string) $menu_item->ID === (string) $menu_item->menu_item_parent ) {
			$menu_item->menu_item_parent = 0;
		}

		$sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
		if ( $menu_item->menu_item_parent ) {
			$menu_items_with_children[ $menu_item->menu_item_parent ] = true;
		}
	}

	// Add the menu-item-has-children class where applicable.
	if ( $menu_items_with_children ) {
		foreach ( $sorted_menu_items as &$menu_item ) {
			if ( isset( $menu_items_with_children[ $menu_item->ID ] ) ) {
				$menu_item->classes[] = 'menu-item-has-children';
			}
		}
	}

	unset( $menu_items, $menu_item );

	/**
	 * Filters the sorted list of menu item objects before generating the menu's HTML.
	 *
	 * @since 3.1.0
	 *
	 * @param array    $sorted_menu_items The menu items, sorted by each menu item's menu order.
	 * @param stdClass $args              An object containing wp_nav_menu() arguments.
	 */
	$sorted_menu_items = apply_filters( 'wp_nav_menu_objects', $sorted_menu_items, $args );

	$items .= walk_nav_menu_tree( $sorted_menu_items, $args->depth, $args );
	unset( $sorted_menu_items );

	// Attributes.
	if ( ! empty( $args->menu_id ) ) {
		$wrap_id = $args->menu_id;
	} else {
		$wrap_id = 'menu-' . $menu->slug;

		while ( in_array( $wrap_id, $menu_id_slugs, true ) ) {
			if ( preg_match( '#-(\d+)$#', $wrap_id, $matches ) ) {
				$wrap_id = preg_replace( '#-(\d+)$#', '-' . ++$matches[1], $wrap_id );
			} else {
				$wrap_id = $wrap_id . '-1';
			}
		}
	}
	$menu_id_slugs[] = $wrap_id;

	$wrap_class = $args->menu_class ? $args->menu_class : '';

	/**
	 * Filters the HTML list content for navigation menus.
	 *
	 * @since 3.0.0
	 *
	 * @see wp_nav_menu()
	 *
	 * @param string   $items The HTML list content for the menu items.
	 * @param stdClass $args  An object containing wp_nav_menu() arguments.
	 */
	$items = apply_filters( 'wp_nav_menu_items', $items, $args );
	/**
	 * Filters the HTML list content for a specific navigation menu.
	 *
	 * @since 3.0.0
	 *
	 * @see wp_nav_menu()
	 *
	 * @param string   $items The HTML list content for the menu items.
	 * @param stdClass $args  An object containing wp_nav_menu() arguments.
	 */
	$items = apply_filters( "wp_nav_menu_{$menu->slug}_items", $items, $args );

	// Don't print any markup if there are no items at this point.
	if ( empty( $items ) ) {
		return false;
	}

	$nav_menu .= sprintf( $args->items_wrap, esc_attr( $wrap_id ), esc_attr( $wrap_class ), $items );
	unset( $items );

	if ( $show_container ) {
		$nav_menu .= '</' . $args->container . '>';
	}

	/**
	 * Filters the HTML content for navigation menus.
	 *
	 * @since 3.0.0
	 *
	 * @see wp_nav_menu()
	 *
	 * @param string   $nav_menu The HTML content for the navigation menu.
	 * @param stdClass $args     An object containing wp_nav_menu() arguments.
	 */
	$nav_menu = apply_filters( 'wp_nav_menu', $nav_menu, $args );

	if ( $args->echo ) {
		echo $nav_menu;
	} else {
		return $nav_menu;
	}
}
125 комментариев
Полезные 15Вопросы 8 Все
    Войти