Новичкам — разбираемся с шаблоном меню horizontal_multilevel

В статье разберем «по строкам» самый частоиспользуемый шаблон компонента bitrix:menu, который позволяет выводить меню произвольного уровня вложенности.

PHP код шаблона

Ниже приведен полный php-код шаблона horizontal_multilevel с подробными комментариями.

<?if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die(); // проверяем, загружена ли служебная часть сайта (ядро)?>
<?
/*
Структура массива $arResult
Массив $arResult состоит из вложенных массивов, соответсвующих пунктам меню.
Порядок следования пунктов
Вложенные массивы содержат ключи:
TEXT - текст текущего пункта меню 
LINK - ссылка текущего пункта меню 
SELECTED - выбран ли пункт меню в данный момент 
PERMISSION - доступ на страницу указанную в $LINK, возможны следующие значения: 
	D - доступ запрещён 
	R - чтение (право просмотра содержимого файла) 
	U - документооборот (право на редактирование файла в режиме документооборота) 
	W - запись (право на прямое редактирование) 
	X - полный доступ (право на прямое редактирование файла и право на изменение прав доступа на данных файл) 
ADDITIONAL_LINKS - дополнительные ссылки для подсветки меню 
ITEM_TYPE - "D" - директория (если LINK заканчивается на "/"), иначе "P" - страница 
ITEM_INDEX - порядковый номер пункта меню 
PARAMS - параметры пунктов меню
DEPTH_LEVEL - уровень вложенности пункта меню (1 для главного, 2 и далее для вложенных)
IS_PARENT - флаг того, что у этого пункта меню будет подменю

Вывод производится вложенными списками вида
<ul id="horizontal-multilevel-menu">
	<li><a>Пункт первого уровня вложенности</a></li>
	<li><a>Пункт первого уровня вложенности</a>
		<ul>
			<li><a>Пункт второго уровня вложенности</a></li>
			<li><a>Пункт второго уровня вложенности</a></li>
		</ul>
	</li>
	<li><a>Пункт первого уровня вложенности</a></li>
</ul>
*/
?>
<?if (!empty($arResult)): // если есть хотя бы 1 пункт меню, можно начинать вывод?>
<ul id="horizontal-multilevel-menu">

<?
$previousLevel = 0; // переменная содержит значение DEPTH_LEVEL предыдущего пункта
foreach($arResult as $arItem): // пробегаем по пунктам, $arItem - массив с информацией о текущем пункте?>

	<?if ($previousLevel && $arItem["DEPTH_LEVEL"] < $previousLevel):?>
	<?// если уровень вложенности текущего пункта меню меньше чем у предыдущего, значит "подменю" закончилось и нужно закрыть список?>
		<?=str_repeat("</ul></li>", ($previousLevel - $arItem["DEPTH_LEVEL"]));?>
	<?endif?>

	<?if ($arItem["IS_PARENT"]): //если пункт содержит подменю, выводим ссылку и начинаем новый список (тег <ul>)?>

		<?if ($arItem["DEPTH_LEVEL"] == 1): // если уровень вложенности =1, т.е. это главное меню?>
			<?// выводим ссылку и добавляем класс "root-item" если пункт неактивный и "root-item-selected" если активный?>
			<li><a href="<?=$arItem["LINK"]?>" class="<?if ($arItem["SELECTED"]):?>root-item-selected<?else:?>root-item<?endif?>"><?=$arItem["TEXT"]?></a>
				<ul>
		<?else: // для остальных уровней вложенности?>
			<?// выводим ссылку и добавляем класс "parent". Если пункт активный, для элемента списка <li> добавляем класс "item-selected"?>
			<li<?if ($arItem["SELECTED"]):?> class="item-selected"<?endif?>><a href="<?=$arItem["LINK"]?>" class="parent"><?=$arItem["TEXT"]?></a>
				<ul>
		<?endif?>

	<?else: // для пунктов, не содержащих подменю?>

		<?if ($arItem["PERMISSION"] > "D"): // проверяем право доступа к пункту?>

			<?if ($arItem["DEPTH_LEVEL"] == 1): // если уровень вложенности =1, т.е. это главное меню?>
				<?// выводим ссылку и добавляем класс "root-item" если пункт неактивный и "root-item-selected" если активный?>
				<li><a href="<?=$arItem["LINK"]?>" class="<?if ($arItem["SELECTED"]):?>root-item-selected<?else:?>root-item<?endif?>"><?=$arItem["TEXT"]?></a></li>
			<?else: // для остальных уровней вложенности?>
				<?// выводим ссылку. Если пункт активный, для элемента списка <li> добавляем класс "item-selected"?>
				<li<?if ($arItem["SELECTED"]):?> class="item-selected"<?endif?>><a href="<?=$arItem["LINK"]?>"><?=$arItem["TEXT"]?></a></li>
			<?endif?>

		<?else: // для пунктов, к которым запрещен доступ?>

			<?if ($arItem["DEPTH_LEVEL"] == 1): // если уровень вложенности =1, т.е. это главное меню?>
				<?// выводим пустую ссылку и добавляем класс "root-item" если пункт неактивный и "root-item-selected" если активный?>
				<li><a href="" class="<?if ($arItem["SELECTED"]):?>root-item-selected<?else:?>root-item<?endif?>" title="<?=GetMessage("MENU_ITEM_ACCESS_DENIED")?>"><?=$arItem["TEXT"]?></a></li>
			<?else: // для остальных уровней вложенности?>
				<?// выводим пустую ссылку и добавляем класс "denied"?>
				<li><a href="" class="denied" title="<?=GetMessage("MENU_ITEM_ACCESS_DENIED")?>"><?=$arItem["TEXT"]?></a></li>
			<?endif?>

		<?endif?>

	<?endif?>

	<?$previousLevel = $arItem["DEPTH_LEVEL"]; // запоминаем уровень вложенности?>

<?endforeach?>

<?if ($previousLevel > 1):// если работа завершилась на пункте меню с уровнем вложенности >1, закрываем вложенные списки?>
	<?=str_repeat("</ul></li>", ($previousLevel-1) );?>
<?endif?>

</ul>
<div class="menu-clear-left"></div>
<?endif?>

CSS код шаблона

Кратко пробежимся по css-файлу и выясним, какие правила за что отвечают при визуальном оформлении меню.

/*
в соответствии со структурой меню, описанной в template.php
контейнер пунтов меню - <ul>
контейнер пункта меню - <li>
ссылка пункта меню - <a>
*/

/**Top menu**/ 
#horizontal-multilevel-menu,#horizontal-multilevel-menu ul /* стили для контейнера пунтов главного и вложенных меню */
#horizontal-multilevel-menu  /* стили для контейнера пунктов главного меню */

/*Links*/ 
#horizontal-multilevel-menu a  /* стили для ссылок пунктов меню всех уровней */
#horizontal-multilevel-menu li  /* стили для контейнера пункта меню */

/*Root items*/ 
#horizontal-multilevel-menu li a.root-item  /* стили для ссылок главного меню */

/*Root menu selected*/ 
#horizontal-multilevel-menu li a.root-item-selected  /* стили для активной (выделенной) ссылки главного меню */

/*Root items: hover*/ 
#horizontal-multilevel-menu li:hover a.root-item, #horizontal-multilevel-menu li.jshover a.root-item  /* стили для ссылок главного меню при наведении мышью */

/*Item-parents*/ 
#horizontal-multilevel-menu a.parent   /* стили для ссылок НЕглавного меню, у которых есть вложенное меню */

/*Denied items*/ 
#horizontal-multilevel-menu a.denied   /* стили для ссылок, к которым запрещен доступ */

/*Child-items: hover*/ 
#horizontal-multilevel-menu li:hover, #horizontal-multilevel-menu li.jshover   /* стили для для контейнера пункта меню при наведении мышью */

/*Child-items selected*/ 
#horizontal-multilevel-menu li.item-selected   /* стили для контейнера активного (выделенного) пункта вложенного меню */

/*Sub-menu box*/ 
#horizontal-multilevel-menu li ul   /* стили для контейнера пунктов вложенного меню */

/*Sub-menu item box*/ 
#horizontal-multilevel-menu li li    /* стили для контейнера пункта вложенного меню */

/*Item link*/ 
#horizontal-multilevel-menu li ul a   /* стили для ссылки пункта вложенного меню */

/*Items text color & size */ 
#horizontal-multilevel-menu li a, 
#horizontal-multilevel-menu li:hover li a, 
#horizontal-multilevel-menu li.jshover li a, 
#horizontal-multilevel-menu li:hover li:hover li a, 
#horizontal-multilevel-menu li.jshover li.jshover li a, 
#horizontal-multilevel-menu li:hover li:hover li:hover li a, 
#horizontal-multilevel-menu li.jshover li.jshover li.jshover li a, 
#horizontal-multilevel-menu li:hover li:hover li:hover li:hover li a, 
#horizontal-multilevel-menu li.jshover li.jshover li.jshover li.jshover li a, 
#horizontal-multilevel-menu li:hover li:hover li:hover li:hover li:hover li a, 
#horizontal-multilevel-menu li.jshover li.jshover li.jshover li.jshover li.jshover li a   /* стили для ссылок вложенного меню */

/*Items text color & size: hover*/ 
#horizontal-multilevel-menu li:hover li:hover a, 
#horizontal-multilevel-menu li.jshover li.jshover a, 
#horizontal-multilevel-menu li:hover li:hover li:hover a, 
#horizontal-multilevel-menu li.jshover li.jshover li.jshover a, 
#horizontal-multilevel-menu li:hover li:hover li:hover li:hover a, 
#horizontal-multilevel-menu li.jshover li.jshover li.jshover li.jshover a 
#horizontal-multilevel-menu li:hover li:hover li:hover li:hover li:hover a, 
#horizontal-multilevel-menu li.jshover li.jshover li.jshover li.jshover li.jshover a 
#horizontal-multilevel-menu li:hover li:hover li:hover li:hover li:hover li:hover a, 
#horizontal-multilevel-menu li.jshover li.jshover li.jshover li.jshover li.jshover li.jshover a   /* стили для ссылок вложенного меню при наведении мышью */

#horizontal-multilevel-menu li ul ul   /* стили для контейнера пунктов вложенного меню (уровень вложенности >2) */

#horizontal-multilevel-menu li:hover ul ul, 
#horizontal-multilevel-menu li.jshover ul ul, 
#horizontal-multilevel-menu li:hover ul ul ul, 
#horizontal-multilevel-menu li.jshover ul ul ul, 
#horizontal-multilevel-menu li:hover ul ul ul ul, 
#horizontal-multilevel-menu li.jshover ul ul ul ul, 
#horizontal-multilevel-menu li:hover ul ul ul ul ul, 
#horizontal-multilevel-menu li.jshover ul ul ul ul ul   /* обработка реакции на наведение мышью - скрываем вложенные меню уровня, более чем на 1 превышающее выделенный */

#horizontal-multilevel-menu li:hover ul, 
#horizontal-multilevel-menu li.jshover ul, 
#horizontal-multilevel-menu li li:hover ul, 
#horizontal-multilevel-menu li li.jshover ul, 
#horizontal-multilevel-menu li li li:hover ul, 
#horizontal-multilevel-menu li li li.jshover ul, 
#horizontal-multilevel-menu li li li li:hover ul, 
#horizontal-multilevel-menu li li li li.jshover ul, 
#horizontal-multilevel-menu li li li li li:hover ul, 
#horizontal-multilevel-menu li li li li li.jshover ul   /* обработка реакции на наведение мышью, показываем вложенные меню */

JS файл шаблона

Этот скрипт существует с одной единственной целью — научить браузер Internet Explorer версии 6 и ниже «понимать» наведение мышью на тег <li>. При наведении элементу присваивается класс «jshover», после «ухода» курсора за пределы элемента — класс снимается.

Заключение

Стандартный шаблон horizontal_multilevel довольно гибкий и позволяет реализовать различные вариации меню правкой только файла стилей. Однако, некоторые необходимые вещи не реализованы — количество пунктов меню n-го уровня можно узнать только полным перебором массива-результата. Еще более сложной задачей является «выделение» в цикле последнего пункта подменю, например, для выделения скругленной рамкой.

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

PS: небольшой привет разработчикам. Этот баг повторяется очень часто при установке продукта из Opera

Если вам понравилась статья, подписывайтесь на обновления блога по rss или присоединяйтесь в twitter

Поделиться ссылкой с друзьями:

Метки: , ,

Категории: Bitrix, Css

Комментарии (44)

  1. Статья — просто must have для таких нубов в Битриксе, как я! А откуда он тянет $arResult?

  2. $arResult передается из компонента как результат его работы

  3. Алексей! Вот столкнулся с задачей вывода последнего пункта меню в отличном от остальных стиле, подскажите пожалуйста как решить эту проблему.

  4. Андрей, нужно пробежать по всем пунктам и запомнить последний (на нужном уровне). Потом при выводе найти его и выделить:)

  5. Игорь:

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

  6. Игорь, вложенность можно задать в настройках компонента

  7. Баг с установкой не только в опере часто встречается. При установке с мака (FF 6.0.2 Chrome Safari 5.1.1) эта ошибка с завидной регулярностью возникает на 75% установки каталога. На Lione’е можно пару раз тыкнуть «Повторить» и поставится, на Snow Leopard’е виснет наглухо.

  8. Тоже новичок ещё в Битриксе. Хочу разобраться с другим типом меню как tree. Можно будет также увидеть разбор этого шаблона. Хотелось бы дописать в него ряд дополнений, но пока разбираюсь ещё только как программировать шаблон.
    пишу его для сайта http://medfarma.net. В css я уже немного поправил.

  9. Almat:

    я тоже новичок в Битриксе. Из Vertical_multilevel или Default можно сделат Аккордион меню?

  10. Almat, можно. Из default легче, но одноуровневое, из vertical-multilevel чуть сложнее, но произвольного уровня вложенности.

  11. Almat:

    я видел вашем интернет магазине http://www.autoleon.ru/ левый аккордион меню. Подскажи какой битриксовый шаблон ты там использовал или костомизировал??
    заранее благодарю!

  12. Это не меню, а сильно измененный catalog.section.list

  13. Almat:

    я могу сделать типа такого http://dobrovoi.ru/files/accordeon1/index.html с помощью vertical-multilevel?? Я делаю интернет магазин чуть-чуть не хватает функционал и дизайн.

  14. Можно ли сделать из этого меню, что нибудь типа вот такого http://radar-groupe.ru? Этот полностью на HTML. Хочу перетащить его под битрикс но вот сомневаюсь насёт меню. А у VW жосткие стандарты (всё по бренд-буку) и не шагу в сторону.

  15. Михаил, можно, только чуть сложнее с выводом.

  16. phisey:

    А не подскажешь как делают, чтобы на странице отображалось два верхних горизонтальных меню одновременно (то есть меню и подменю)? Пример http://www.ursu.ru
    Какой шаблон используют?
    Обычно ведь при нажатии кнопки меню, выходит подменю, а меню пропадает.

  17. phisey, нужно модифицировать шаблон компонента меню. Примерно так: определяем активный пункт меню первого уровня — запоминаем его подпункты (отмечая активный, если он есть). Проходим по меню и выводим в строку 1й уровень, затем запомненные подпункты 2й строкой.

  18. Андрей:

    Алексей, Здравствуйте!
    Понимаю что статья полезная, но к сожалению плохо знаком с PHP, с Битриксом связался буквально пару дней назад, стоит задача сделать всплывающее меню.
    Но у меня ничего не выходит, чесно сказать удивлен что такая простейшая вещь требует дополнительных телодвижений.
    Запутался окончательно. Не могли бы вы подсказать пошагово как его реализовать?

  19. Алексей Валеев:

    Дефолтный шаблон horizontal_multilevel уже содержит механизм «выпадания» меню 2го и более уровней. Ничего дополнительно в большинстве случаев делать не нужно, только стилизовать с помощью css

  20. Vladimir:

    В настройках компонента максимальный уровень вложенности — 4, а мне надо 5, как поправить?

  21. Можно попробовать руками поставить нужную цифру в режиме редактирования страницы как php. Но меню строится где-то в недрах битрикса, поэтому не факт что сработает.

  22. Сергей:

    Алексей, добрый день, подскажите плиз, а как сделать (куда именно прописать в шаблоне компонента) меню двухколоночное, т.е. при наведении на пункт меню, выпадает не один столбец а два или больше (как на сайте eldorado.ru например). Спасибо!

  23. Mike:

    Добрый день, как сделать неактивными первый пункт меню?

  24. Алексей Валеев:

    Mike, с ходу могу предложить 2 варианта:
    1. В шаблоне компонента сделать счетчик и по его значению деативировать пункт.
    2. В расширенном режиме редактирования меню ввести параметр и по его значению в шаблоне меню деактивировать нужные пункты.

  25. Андрей:

    подменю получается слишком длинным, хочу сделать в 2 колонки подскажи как это сделать

  26. Андрей:

    Алексей приведите пример «сделать счетчик и по его значению деативировать пункт». Просто много вариантов перепробовал, они не работают + счет ведется во всех меню (не могу сделать сброс когда переходим в след пункт меню, и например число шесть это 3 пункта предыдущего меню + 3 текущего)

  27. Артём:

    $index = $arItem["DEPTH_LEVEL"];
    $count[$index]++;

    $count[1] — счётчик меню первого уровня вложенности
    $count[2] — счётчик меню второго уровня вложенности

  28. RodionFS:

    Читал битриксовский меню-туториал (официальный мануал, между прочим) про изменение шиблона и стилей и ничего не понял, как подправить шаблон, чтобы вложенные меню не «съезжали» и перенос нормально работал. Может я тупой? :)
    Прочет CSS код шаблона в этой статье и понял ВСЕ! Автору респект!

  29. Itachi261092:

    Здравствуйте. большое спасибо за такую подробную и понятную статью. я бы хотел кое что уточнить что относится не совсем к шаблону. просто я не могу разобраться почему так и как это исправить. проблема в следующем:
    у меня в меню ссылки расписаны по примеру:
    главная=»/ru/»
    первая ссылка меню=»/ru/first/»
    вторая ссылка меню=»/ru/second» и т.д. и при этом у меня не зависимо от расположения в данный момент, ссылка главная остаётся активной. то есть если я нажму на 2 или 3 ссылку, будет активна она и главная. страница при этом открывается по нажато ссылке. подскажите как исправить такую вещь?

  30. Itachi261092:

    Можно не отвечать. справился. в вызове компонента есть пункт отвечающий за мультивыделение ссылок — отключил его и всё норм.

  31. Света:

    Алексей, здравствуйте!

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

    Подскажите, пожалуйста, решение задачи: у меня 2-хуровневное меню. В 1-м уровне последний пункт должен иметь свой стиль. Я прописала код:
    $aItem ): ?>

    <a href="»> //стиль для последнего пункта

    <a href="»> //стиль для пунктов, кроме последнего


    но условие не работает. Подскажите, пожалуйста, как применить стиль?

  32. Света:

    К предыдущему сообщению:

    Я прописала код:
    foreach( $arResult as $key => $aItem )

    if ($arItem[DEPTH_LEVEL] == 1)
    if( $key == (count($arResult)- 1))
    TEXT //стиль для последнего пункта
    else
    TEXT> //стиль для пунктов, кроме последнего
    endif

    но условие не работает. Подскажите, пожалуйста, как применить стиль?

  33. Тхалгат:

    огромное спасибо! все разжовано как надо!

  34. Здравствуйте, подскажите как уже созданную страницу добавить в выпадающий список? есть куча созданных страниц, надо их прикрепить к меню (меню изначально было написано в html и указывало на страницы ) теперь надо все страницы прикрепить к горизонтальному меню, сколько пробую никак не получается, при выборе пункта добавить в меню — предлагает верхнее, левое или нижнее. Мне надо добавить некоторые страницы в верхний уровень, а большинство в выпадающий. Как это сделать?

  35. Наталья:

    Большое спасибо за комментарии к коду! Очень помогло!

  36. Виталий:

    Отлично разжевано, теперь все четко и ясно. Спасибо:)

  37. Ярослав:

    Спасибо Алексей Валеев за статью. Комментарии к коду позволили в сжатые сроки сделать свое меню.

  38. Олег:

    В приведенном коде имеется ошибка: для пунктов меню, у которых установлен IS_PARENT, права доступа не проверяются.

  39. Андрей:

    В самом деле Не чего не понятно!

  40. Доброво времени суток.
    Я новичок.
    Подскажите где ошибка.

    <li class=»dropdown active» ><a href="» class=»dropdown-toggle» data-toggle=»dropdown» data-hover=»dropdown» data-delay=»0″ data-close-others=»false»>

    <li class=»dropdown active» ><a href="» class=»dropdown-toggle» data-toggle=»dropdown» data-hover=»dropdown» data-delay=»0″ data-close-others=»false»>

    Где ошибка???
    Не возвращается ссылка LINK

  41. Анд:

    а как можно сделать .current для самой первой, главной кнопки top меню битрикс шаблона ?

  42. Павел.:

    мне нужно создать такое меню
    http://jemand.ru/examples/vertikalnoe-vypadayushhee-menyu-cssjs-onclick.html#
    но что-то я не могу разобраться что куда.
    помогите.

  43. Андрей:

    А про шаблоны компонентов как catalog.section.list и catalog.list такое будет? Очень бы хотелось

  44. Здравствуйте Алексей!
    Я делаю меню из catalog.section.list, как можно сделать чтоб подсвечивался активный раздел по аналогии $arItem["SELECTED"], и как сделать проверку на родителя по аналогии $arIrem["IS_PARENT"]. Подскажите пожалуйста если вас не затруднит!