Разработка мегаменю на 1С-Битрикс
Стандартный компонент bitrix:menu выводит плоский список ссылок. Крупный интернет-магазин требует другого: многоколоночная навигация по разделам, быстрый доступ ко второму и третьему уровню иерархии без лишних кликов, поддержка тысяч категорий без деградации производительности. Это мегаменю — отдельный компонент поверх инфоблока разделов.
Источник данных: дерево разделов
Структура разделов Битрикс хранится в b_iblock_section. Для мегаменю нужны два-три уровня вложенности. Стандартный метод CIBlockSection::GetList с параметром INCLUDE_SUBSECTIONS = 'N' не даёт дерево — только плоский список. Строим дерево вручную:
// /local/lib/Menu/MegaMenuBuilder.php
namespace Local\Menu;
class MegaMenuBuilder
{
public function build(int $iblockId, int $depth = 3): array
{
$cache = new \CPHPCache();
$cacheId = 'megamenu_' . $iblockId . '_' . LANGUAGE_ID;
if ($cache->InitCache(3600, $cacheId, '/megamenu/')) {
return $cache->GetVars()['menu'];
}
$raw = $this->fetchSections($iblockId, $depth);
$tree = $this->buildTree($raw);
$cache->StartDataCache();
$cache->EndDataCache(['menu' => $tree]);
return $tree;
}
private function fetchSections(int $iblockId, int $depth): array
{
$result = [];
$res = \CIBlockSection::GetList(
['LEFT_MARGIN' => 'ASC'],
[
'IBLOCK_ID' => $iblockId,
'ACTIVE' => 'Y',
'DEPTH_LEVEL' => [$depth], // до нужного уровня
'GLOBAL_ACTIVE' => 'Y',
],
false,
['ID', 'NAME', 'CODE', 'DEPTH_LEVEL', 'IBLOCK_SECTION_ID',
'SECTION_PAGE_URL', 'PICTURE', 'UF_MENU_ICON', 'LEFT_MARGIN', 'RIGHT_MARGIN']
);
while ($section = $res->GetNext()) {
$result[$section['ID']] = $section;
}
return $result;
}
private function buildTree(array $flat, int $parentId = 0): array
{
$tree = [];
foreach ($flat as $section) {
if ((int)$section['IBLOCK_SECTION_ID'] === $parentId) {
$section['children'] = $this->buildTree($flat, (int)$section['ID']);
$tree[] = $section;
}
}
return $tree;
}
}
Компонент мегаменю
Компонент регистрируется в /local/components/local/megamenu/. Шаблон принимает дерево разделов и рендерит HTML:
// /local/components/local/megamenu/class.php
class LocalMegaMenuComponent extends \CBitrixComponent
{
public function onPrepareComponentParams($params): array
{
$params['IBLOCK_ID'] = (int)($params['IBLOCK_ID'] ?? CATALOG_IBLOCK_ID);
$params['DEPTH'] = (int)($params['DEPTH'] ?? 3);
return $params;
}
public function executeComponent(): void
{
$builder = new \Local\Menu\MegaMenuBuilder();
$this->arResult['MENU'] = $builder->build(
$this->arParams['IBLOCK_ID'],
$this->arParams['DEPTH']
);
$this->setFrameMode(false); // отключаем edit mode Битрикс
$this->includeComponentTemplate();
}
}
Шаблон мегаменю
// /local/components/local/megamenu/templates/.default/template.php
/** @var array $arResult */
?>
<nav class="megamenu" aria-label="Навигация по каталогу">
<ul class="megamenu__list">
<?php foreach ($arResult['MENU'] as $category): ?>
<li class="megamenu__item" data-id="<?= $category['ID'] ?>">
<a href="<?= htmlspecialchars($category['SECTION_PAGE_URL']) ?>"
class="megamenu__link">
<?php if ($category['UF_MENU_ICON']): ?>
<img src="<?= \CFile::GetPath($category['UF_MENU_ICON']) ?>"
alt="" class="megamenu__icon" loading="lazy">
<?php endif ?>
<span><?= htmlspecialchars($category['NAME']) ?></span>
</a>
<?php if (!empty($category['children'])): ?>
<div class="megamenu__dropdown">
<div class="megamenu__columns">
<?php foreach (array_chunk($category['children'], 8) as $col): ?>
<div class="megamenu__col">
<?php foreach ($col as $sub): ?>
<a href="<?= htmlspecialchars($sub['SECTION_PAGE_URL']) ?>"
class="megamenu__sublink">
<?= htmlspecialchars($sub['NAME']) ?>
</a>
<?php if (!empty($sub['children'])): ?>
<ul class="megamenu__tertiary">
<?php foreach (array_slice($sub['children'], 0, 5) as $third): ?>
<li>
<a href="<?= htmlspecialchars($third['SECTION_PAGE_URL']) ?>">
<?= htmlspecialchars($third['NAME']) ?>
</a>
</li>
<?php endforeach ?>
</ul>
<?php endif ?>
<?php endforeach ?>
</div>
<?php endforeach ?>
</div>
</div>
<?php endif ?>
</li>
<?php endforeach ?>
</ul>
</nav>
CSS: открытие без JavaScript
Базовая работа мегаменю через CSS :hover — жест доброй воли к пользователям с медленным JS:
.megamenu__dropdown {
position: absolute;
top: 100%;
left: 0;
width: 100%;
background: #fff;
border-top: 2px solid var(--color-primary);
box-shadow: 0 8px 24px rgba(0,0,0,.12);
display: none;
z-index: 1000;
}
.megamenu__item:hover .megamenu__dropdown,
.megamenu__item:focus-within .megamenu__dropdown {
display: block;
}
.megamenu__columns {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 1.5rem;
padding: 1.5rem;
max-width: 1200px;
margin: 0 auto;
}
JavaScript: задержка закрытия и доступность
// Задержка 200мс на закрытие — чтобы не мигало при перемещении мыши
document.querySelectorAll('.megamenu__item').forEach(item => {
let timer;
item.addEventListener('mouseenter', () => {
clearTimeout(timer);
item.querySelector('.megamenu__dropdown')?.style.setProperty('display', 'block');
});
item.addEventListener('mouseleave', () => {
timer = setTimeout(() => {
item.querySelector('.megamenu__dropdown')?.style.setProperty('display', 'none');
}, 200);
});
});
// Клавиатурная навигация
document.querySelectorAll('.megamenu__link').forEach(link => {
link.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') {
const dropdown = link.nextElementSibling;
if (dropdown) {
e.preventDefault();
dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
}
}
});
});
Кеширование и инвалидация
Дерево разделов кешируется на 3600 секунд. При изменении любого раздела каталога — обработчик события OnAfterIBlockSectionUpdate сбрасывает кеш:
AddEventHandler('iblock', 'OnAfterIBlockSectionUpdate', function($fields) {
$cache = new \CPHPCache();
$cache->CleanDir('/megamenu/');
});
Производительность
Дерево разделов строится одним запросом к b_iblock_section, результат закеширован. На странице мегаменю не генерирует дополнительных SQL-запросов. При 500 разделах в трёх уровнях вложенности запрос занимает 3-8 мс, построение дерева в PHP — ещё 2-5 мс. Полный render закешированного мегаменю — менее 1 мс.
Сроки реализации
| Конфигурация | Срок |
|---|---|
| Базовое мегаменю (2 уровня, hover) | 3–4 дня |
| С третьим уровнем, кешированием, инвалидацией | 5–7 дней |
| Адаптивная версия с мобильным drawer | +3–4 дня |
| С изображениями, баннерами, акциями | +2–3 дня |







