WordPress как на ладони
Наставник Трепачёв Д.П., phphtml.net wordpress jino

Произвольное меню в WP 3.0+ (wp_nav_menu)

Широко известно, что в WordPress 3.0 добавлена поддержка произвольных меню (настраиваемых меню). Вещь, на мой взгляд, крайне удобная и полезная. Собственно, отсюда и эта статья.

Удобство заключается в том, что теперь можно создавать и конфигурировать меню прямо из админки, добавляя ссылки кликами по чекбоксам и меняя порядок ссылок простым перетаскиванием. В меню можно добавить ссылки на страницы, категории и отдельные посты. Можно создавать многоуровневые меню, так же в меню можно добавлять свои произвольные ссылки, о которых WordPress не знает. В общем, полная свобода действий.

Однако, чтобы такая "свобода" была доступна, нужно, скажем так, легким движением мышки, настроить вывод произвольного меню в шаблон.

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

Заметка автора: огорчает, то что меню работает через таксономию (nav_menu) WordPress, а произвольные (внешние) ссылки, записываются в основную таблицу БД posts. Такой подход более гибкий и динамичный, однако требует постоянной генерации таких меню, поэтому такие меню создают приличную нагрузку. Такая уж концепция WordPress - удобство превыше всего, несмотря на то, что порой оно не оправдано производительностью.

Итак, приступим.

к началу

Включаем поддержку произвольных меню (wp_nav_menu)

Для начала нужно зарегистрировать возможность использования произвольных меню и сами меню. Делается это в файле functions.php,с помощью функции register_nav_menu(), так:

register_nav_menus(array(
	'top'    => 'Верхнее меню',    //Название месторасположения меню в шаблоне
	'bottom' => 'Нижнее меню'      //Название другого месторасположения меню в шаблоне
));

Сейчас мы зарегистрировали 2 меню с идентификаторами  'top' и 'bottom' с соответствующими им названиями. Идентификаторы нужны, чтобы использовать их в теме, для указания того места, где, через функцию вывода wp_nav_menu(), будет выводиться созданное в админке меню. Названия зарегистрированных расположений мы увидим в админке, когда зайдем в раздел Внешний вид -> Меню.

Зарегистрированные места расположений меню

После того, как меню зарегистрированы, идем в админку и создаем свои меню (в данном примере 2 менюшки):

  1. Задаем название меню (меню в шаблоне можно выводить по указанному названию, функцией wp_nav_menu()

  2. Создаем пункты меню. Используем левый блок: страницы ссылки, рубрики

  3. Выбираем где будет расположено меню, так как мы зарегистрировали 2 менюшки, у нас будет 2 варианта: "Верхнее меню" и "Нижнее меню".

Создание произвольного меню в WordPress. Админ-панель.

Поддержка произвольных меню в WordPress включается для каждой темы отдельно, такой строчкой в файле functions.php add_theme_support('menus'); Однако, в этой строчке нет необходимости, если мы регистрируем меню. В этом случаем поддержка будет включена автоматически.

к началу

Вывод произвольных меню через функцию wp_nav_menu

Меню зарегистрированы и созданы, осталось добавить их в шаблон. Делается это функцией wp_nav_menu(), которая может принимать следующие параметры:

$args = array(
	'menu'            => '',              // (string) Название выводимого меню (указывается в админке при создании меню, приоритетнее 
										  // чем указанное местоположение theme_location - если указано, то параметр theme_location игнорируется)
	'container'       => 'div',           // (string) Контейнер меню. Обворачиватель ul. Указывается тег контейнера (по умолчанию в тег div)
	'container_class' => '',              // (string) class контейнера (div тега)
	'container_id'    => '',              // (string) id контейнера (div тега)
	'menu_class'      => 'menu',          // (string) class самого меню (ul тега)
	'menu_id'         => '',              // (string) id самого меню (ul тега)
	'echo'            => true,            // (boolean) Выводить на экран или возвращать для обработки
	'fallback_cb'     => 'wp_page_menu',  // (string) Используемая (резервная) функция, если меню не существует (не удалось получить)
	'before'          => '',              // (string) Текст перед <a> каждой ссылки
	'after'           => '',              // (string) Текст после </a> каждой ссылки
	'link_before'     => '',              // (string) Текст перед анкором (текстом) ссылки
	'link_after'      => '',              // (string) Текст после анкора (текста) ссылки
	'depth'           => 0,               // (integer) Глубина вложенности (0 - неограничена, 2 - двухуровневое меню)
	'walker'          => '',              // (object) Класс собирающий меню. Default: new Walker_Nav_Menu
	'theme_location'  => ''               // (string) Расположение меню в шаблоне. (указывается ключ которым было зарегистрировано меню в функции register_nav_menus)
);

В данном примере в шаблон нужно вставить примерно (зависит от необходимых вам параметров) такие, 2 кода:

к началу

#1. Вывод меню по расположению

Верхнее меню. Вставляем в шапку шаблона (header.php), там где будет выводится верхнее (top) меню:

<?php 
wp_nav_menu( array(
	'menu_class'=>'menu',
    'theme_location'=>'top',
    'after'=>' /'
) );
?>

Выведет созданное в админке меню, прикрепленное к расположению "Верхнее меню" с подобной структурой:

<div class='<классы WP>'>
	<ul class='menu'>
		<li><a class='<классы WP>' href="#">Анкор ссылки</a> /</li>
		<li><a class='<классы WP>' href="#">Анкор ссылки</a> /</li>
		<li><a class='<классы WP>' href="#">Анкор ссылки</a> /</li>
	</ul>
</div>

Нижнее меню. Вставляем в подвал шаблона (footer.php), там где будет выводится нижнее (bottom) меню:

<?php wp_nav_menu('menu_class=bmenu&theme_location=bottom'); ?>

Выведет созданное в админке меню, прикрепленное к расположению "Нижнее меню". Структура будет идентичная первой.

Обратите внимание, в первом варианте параметры были переданы через массив (array). Во втором через строку. Оба варианта правильны. Это обычное дело для функций WordPress - параметры можно передавать как массивом, так и строкой (строка потом преобразовывается в массив).

к началу

#2 Выводим меню по названию

Чтобы вывести меню по его названию можно воспользоваться аргументом 'menu'. Название указывается, то которое было задано при создании меню в админке. В нашем примере (см. картинку) "Главное меню". Аргумент menu обладает большим приоритетом чемtheme_location, а значит, если мы выводим по названию, то параметр theme_location будет игнорироваться.

<?php wp_nav_menu('menu=Главное меню'); ?>

Можно указать ID меню, а не название. Так, при изменении названия меню, код останется рабочим. ID меню можно посмотреть в УРЛ во время редактирования меню:

<?php wp_nav_menu('menu=455'); ?>
к началу

Заметки

Уберем обертку Div

Вы наверное обратили внимание, что меню "оборачивается", часто, ненужным тегом div. Его можно удалить, указав в аргументах для функции wp_nav_menu() пустой параметр 'container'=>''.

Изменяем параметры по умолчанию

Чтобы постоянно не указывать один и те же параметр для вставляемых меню, их можно переопределить в functions.php. Делается это через фильтр wp_nav_menu_args:

register_nav_menus(array(
	'top'    => 'Верхнее меню',
	'bottom' => 'Нижнее меню'
));

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

По аналогии, можно создать свои аргументы по умолчанию: $args['аргумент'] = 'значение'.

Проверка зарегистрировано ли меню

В WordPress так же есть, функция условия: has_nav_menu('top') - проверяет было ли зарегистрировано расположение меню top. Если меню не указано, то функция wp_nav_menu() сработает, как wp_list_pages(), но "обворачиватель" div останется, несмотря на то что в аргументах мы его убрали. Решить эту проблему можно так:

if (has_nav_menu('top')){
  wp_nav_menu( array(
		'container' => '',
		'theme_location' => 'top',
		'menu_class' => 'menu')
	);
} else {
	echo '<ul class="menu">';
	wp_list_pages( array('depth' => 1, 'title_li' => '' ));
	echo '</ul>';
}

Параметр walker

Из всех передаваемых аргументов, непонятным является walker. Для тех, кто хочет разобраться для чего он нужен, читайте раздел в описании функции wp_nav_menu(). Там коротко и ясно описан принцип. Если очень коротко, то с его помощью можно внедриться в процесс генерации меню и изменить его как угодно.

Включение доп. параметров у меню

Меню можно настроить, например, можно добавить возможность указывать CSS класс для элемента меню, для ссылки меню. Для этого откройте вкладку «Настройки экрана»:

-

Удобных вам менюшек gamer

Продвижение и раскрутка сайтов www.seop.ru

продвижение и раскрутка сайтов www.seop.ru

www.seop.ru

Произвольное меню в WP 3.0+ (wp_nav_menu) 62 комментария
Полезные 4 Вопросы 1 Все
  • Александр cайт: game.tobefun.org

    Здравствуйте. Скажите, каким образом можно реализовать добавление в меню ещё одного пункта со статическим содержимым.
    Т.е. Нужно получать меню такого рода

    <div id="menu">
    	<ul>
    		<li><a href="#">Главная</a></li>
    		<li><a href="#">Оплата и доставка</a></li>
    		<li><a href="#">Гарантия</a></li>
    		<li><a href="#">Помощь</a></li>
    		<li><a href="#">Контакты</a></li>
    		<li>
    			<form>
    			  <span>
    			   <input type="text" class="search rounded" placeholder="Поиск..."/>
    			  </span>
    			</form>
    		</li>
    	</ul>
    </div>

    Как видите в конце есть форма поиска. Как сделать так, чтобы она всегда автоматически добавлялась для данного меню ?

    Ответить4.1 года назад #
    • Ольга

      Александр, нашли как сделать? если нашли, поделитесь рецептом, пжл
      ну а если не нашли, сама подумаю, поделюсь

      Ответить3.9 года назад #
    • VRS

      Отключите обертку меню тегами и вручную пропишите их в начале и в конце, поставив последним пунктом li форму поиска. Только невалиден скорее всего тег внутри li.

      1
      Ответить3.9 года назад #
  • Сергей
    @

    здравствуйте! Скажите пожалуйста, а как так сделали, что у вас поисковая форма сайта все время на виду? если конечно не трудно ответить. thank_you

    Ответить3.8 года назад #
    • Kama4457

      В двух словах не смогу удовлетворяюще ответить: написал скрипт на jQuery. В сети должна быть информация по этому вопросу.

      1
      Ответить3.8 года назад #
  • игорь

    А как можно обернуть ссылку в тег

    <h1>

    например?

    Ответить3.5 года назад #
  • Здравствуйте!
    Такой вопрос, если мне из-за особенностей дизайна нужно изменить html-код меню. Т е в каждый пункт нужно дописать span:

    <li><span></span><a href="..."></a></li>

    Спасибо)

    Ответить2.6 года назад #
    • Уже решила вопрос. Все оказалось просто - в поле "текст", в самом пункте меню можно задать не только текст, но и html.

      Ответить2.6 года назад #
  • Dzmitry Roshchyn cайт: pribylvseti.ru

    Как сделать выпадающее меню? Я поставил нужные странцы под одну и теперь они выходятся сходу, а не при наведении. Что подкрутить, Тимур?

    Ответить1.4 года назад #
  • Dzmitry Roshchyn cайт: pribylvseti.ru

    Ещё вопрос! Что оптимальней собственное меню или воспользоваться произвольным "Вордпресс"? Немогу в последнем варианте с выводом иерархии пунктов меню разобраться, нужно выпадающее.

    1
    Ответить1.4 года назад #
    • Kama4457

      Смотря какой меню нужно в итоге, если простое и неизменяемое, то наверное легче свое сделать. Если посложнее с иерархией и которое может меняться, то лучше разобраться в меню ВП. Настройка там сложнее наверное, но потом зато удобно в админке менять все...

      1
      Ответить1.4 года назад #
  • Артем

    Привет всем)
    что нужно вставать, чтобы ссылка не ссылалась на себя. Т.е находишься на странице из меню ,а ссылка в меня активная.. нужно сделать чтобы не активная

    http://prntscr.com/cesygb

  • Артем

    Отличный мануал, Огромное спасибо. Есть вопросик: Как сделать так что бы произвольное меню отображалось только в определенных материалах, к примеру мы открываем материал "О компании" и там пункты меню слева, а в другом материале нет ничего. Заранее спасибо.

    • Kama4457

      Регни несколько меню и через условные теги выводи нужное меня в нужных местах.

      if( is_page('about') ){
      	// меню о компании
      	wp_nav_menu( параметры );
      }
      else {
      	// другое меню
      	wp_nav_menu( параметры );
      }
  • Здравствуйте. Подскажите пожалуйста, как присвоить свой класс для активного пункта меню? Т.к. в данный момент это дефолтный класс current-menu-item.

    И сразу второй вопрос, но более важный. Перешел на именование классов по БЭМ и меню выглядит вот таким образом:

    <ul class="list">
    	<li class="list__item"><a href="#" class="list__link">Пункт 1</a></li>
    	<li class="list__item"><a href="#" class="list__link">Пункт 2</a></li>
    	<li class="list__item"><a href="#" class="list__link">Пункт 3</a></li>
    </ul>

    Но на данный момент WordPress генерит кучу своих классов, а хочется реализовать свой способ.
    Очень надеюсь на помощь. Спасибо!

    • campusboy1712 cайт: wp-plus.ru
      @

      Приветствую. Тут можно использовать аргумент walker, который описываете так, как хотите видеть структуру меню. Именно благодаря walker в WP делаются меню в стиле Бутстрап и так далее, то есть меню можно сделать каким угодно. Если же дело касается лишь в изменении классов, то можно воспользоваться хуками. Я не использую БЭМ, но данные советы можно адаптировать под себя. Я создал меню из 3 пунктов новостей. Итак, вот начальный код:

      add_filter('nav_menu_css_class', 'my_css_attributes_filter', 100, 1);
      add_filter('nav_menu_item_id', 'my_css_attributes_filter', 100, 1);
      add_filter('page_css_class', 'my_css_attributes_filter', 100, 1);
      function my_css_attributes_filter($var){
      	var_dump($var);
      	return is_array($var) ? array() : '';
      }

      Код вставлен в functions.php. Сам по себе эта "конструкция" обнуляет классы у li элементов, они в меню становятся "чистыми", но наша задача ведь их заменить на свои, поэтому я в код добавил var_dump($var), чтобы посмотреть, какие данные приходят. Смотрим:

      array(5) {
        [1]=>
        string(9) "menu-item"
        [2]=>
        string(24) "menu-item-type-post_type"
        [3]=>
        string(21) "menu-item-object-post"
        [4]=>
        string(17) "current-menu-item"
        [5]=>
        string(14) "menu-item-3056"
      }
      string(14) "menu-item-3056"
      array(4)
      {
      	[1]=>
      	string(9) "menu-item"
      	[2]=>
      	string(24) "menu-item-type-post_type"
      	[3]=>
      	string(21) "menu-item-object-post"
      	[4]=>
      	string(14) "menu-item-3057"
      }
      string(14) "menu-item-3057"
      array(4)
      {
      	[1]=>
      	string(9) "menu-item"
      	[2]=>
      	string(24) "menu-item-type-post_type"
      	[3]=>
      	string(21) "menu-item-object-post"
      	[4]=>
      	string(14) "menu-item-3058"
      }
      string(14) "menu-item-3058"

      По логу переменных видно, что я находился на новости 3056, потому в отличие от других массив, относящимся к другим пунктам меню, есть дополнительный класс "current-menu-item". Затем в самом коде я как раз таки обнуляю всё это дело, потому li становятся чистыми. Отсюда вывод - можно составить список тех классов, которые должны быть выведены или же подменены. Так же можно видеть, что часть переменных - это массивы, а часть - строки. Массивы - это классы для li, а строки - id.

      Пробуем подогнать по Ваш пример, где есть пожелание сделать под БЭМ:

      add_filter('nav_menu_css_class', 'my_css_attributes_filter', 100, 1);
      add_filter('nav_menu_item_id', 'my_css_attributes_filter', 100, 1);
      add_filter('page_css_class', 'my_css_attributes_filter', 100, 1);
      function my_css_attributes_filter($var){
      	if( is_array($var) ){
      		// сюда попадает массив со всеми классами li элемента
      		// зададим массив, в который будем складировать нужные нам классы, а также сразу зададим, какой будет класс у всех li элементов
      		$all_class_li = array('list__item');
      		foreach($var as $class_li){
      			if( $class_li == 'current-menu-item' ){
      				$all_class_li[] = 'list__item_current'; // класс активного меню
      			}
      		}
      		return $all_class_li;
      	}else{
      		// сюда попадает ID li элемента, мы его просто "затираем" пустой строкой
      		return '';
      	}
      }

      Получится что-то следующие:

      <ul class="primary-menu" id="menu-name_menu">
        <li class="list__item list__item_current">
      	<a href="http://test-wp.ru/news-777/">Статейка 777</a>
        </li>
        <li class="list__item">
      	<a href="http://test-wp.ru/news-999/">Статейка 999</a>
        </li>
        <li class="list__item">
      	<a href="http://test-wp.ru/news-10/">Статейка 10</a>
        </li>
      </ul>

      Возможно кто-то подскажет ещё хуки для лёгкой подмены классов, чтобы не заморачиваться с walker, хотя walker - это самое гибкое решение для модификации меню.

      1
  • Николай

    Подскажите, как можно добавить к каждому отдельному пункту меню свою отдельную иконку. При помощи 'link before' для всех пунктов добавляется одна и та же.

    Ответить3 месяца назад #

Здравствуйте, !

Ваш комментарий