Розробка адаптивного мегаменю для мобільних 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Розробка адаптивного мегаменю для мобільних 1С-Бітрікс
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Розробка адаптивного мегаменю для мобільних 1С-Бітрікс

Десктопне мегаменю з hover-дропдаунами на мобільному не працює: немає hover, немає місця для багатоколонкового layout. Потрібна окрема UX-концепція для мобільних: drawer (висувна панель), ієрархічне меню з переходами між рівнями, touch-жести. Код один, поведінка — різна.

Концепція: drawer + slide-based навігація

На мобільних категорії першого рівня займають всю ширину екрана. Тап по категорії з підкатегоріями — анімований перехід на наступний екран. Назад — свайп або кнопка. На десктопі той самий HTML працює як hover-дропдаун через CSS.

HTML-структура (єдина для обох варіантів)

<!-- Тригер мобільного меню -->
<button class="menu-toggle" aria-expanded="false" aria-controls="site-nav" aria-label="Меню">
    <span class="menu-toggle__bar"></span>
    <span class="menu-toggle__bar"></span>
    <span class="menu-toggle__bar"></span>
</button>

<nav id="site-nav" class="megamenu" aria-label="Основна навігація" aria-hidden="true">
    <div class="megamenu__backdrop"></div>

    <div class="megamenu__panel megamenu__panel--root is-active" data-level="0">
        <div class="megamenu__head">
            <span class="megamenu__title">Каталог</span>
            <button class="megamenu__close" aria-label="Закрити меню">×</button>
        </div>
        <ul class="megamenu__list">
            <?php foreach ($arResult['MENU'] as $category): ?>
            <li class="megamenu__item">
                <a href="<?= $category['SECTION_PAGE_URL'] ?>"
                   class="megamenu__link
                          <?= !empty($category['children']) ? 'has-children' : '' ?>"
                   <?php if (!empty($category['children'])): ?>
                       data-panel="cat-<?= $category['ID'] ?>"
                       aria-haspopup="true"
                   <?php endif ?>>
                    <?= htmlspecialchars($category['NAME']) ?>
                    <?php if (!empty($category['children'])): ?>
                    <svg class="megamenu__arrow" aria-hidden="true" width="16" height="16">
                        <path d="M6 4l4 4-4 4" fill="none" stroke="currentColor" stroke-width="1.5"/>
                    </svg>
                    <?php endif ?>
                </a>
            </li>
            <?php endforeach ?>
        </ul>
    </div>

    <?php foreach ($arResult['MENU'] as $category): ?>
    <?php if (!empty($category['children'])): ?>
    <div class="megamenu__panel" id="panel-cat-<?= $category['ID'] ?>" data-level="1">
        <div class="megamenu__head">
            <button class="megamenu__back" aria-label="Назад">
                <svg width="16" height="16"><path d="M10 4L6 8l4 4" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>
            </button>
            <a href="<?= $category['SECTION_PAGE_URL'] ?>" class="megamenu__title">
                <?= htmlspecialchars($category['NAME']) ?>
            </a>
        </div>
        <ul class="megamenu__list">
            <li>
                <a href="<?= $category['SECTION_PAGE_URL'] ?>" class="megamenu__link megamenu__link--all">
                    Всі товари розділу
                </a>
            </li>
            <?php foreach ($category['children'] as $sub): ?>
            <li>
                <a href="<?= $sub['SECTION_PAGE_URL'] ?>" class="megamenu__link">
                    <?php if ($sub['MENU_IMAGE_SRC']): ?>
                    <img src="<?= $sub['MENU_IMAGE_SRC'] ?>" alt="" width="40" height="40" loading="lazy">
                    <?php endif ?>
                    <?= htmlspecialchars($sub['NAME']) ?>
                </a>
            </li>
            <?php endforeach ?>
        </ul>
    </div>
    <?php endif ?>
    <?php endforeach ?>
</nav>

CSS: drawer на мобільних, hover на десктопі

/* === Мобільні (до 1024px) === */
.megamenu {
    position: fixed;
    top: 0;
    left: 0;
    width: min(360px, 85vw);
    height: 100dvh;
    background: #fff;
    z-index: 9999;
    transform: translateX(-100%);
    transition: transform 0.3s cubic-bezier(.4,0,.2,1);
    overflow: hidden;
}

.megamenu.is-open {
    transform: translateX(0);
}

.megamenu__backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.5);
    z-index: -1;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.3s;
}

.megamenu.is-open .megamenu__backdrop {
    opacity: 1;
    pointer-events: auto;
}

.megamenu__panel {
    position: absolute;
    inset: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: #fff;
    transform: translateX(100%);
    transition: transform 0.25s ease;
}

.megamenu__panel.is-active {
    transform: translateX(0);
}

.megamenu__panel--root {
    transform: translateX(0);
}

.megamenu__panel--root.slide-out {
    transform: translateX(-30%);
}

/* === Десктоп (від 1024px) === */
@media (min-width: 1024px) {
    .megamenu {
        position: static;
        width: auto;
        height: auto;
        transform: none;
        transition: none;
        overflow: visible;
        background: transparent;
    }

    .megamenu__backdrop,
    .megamenu__close,
    .megamenu__back,
    .menu-toggle { display: none; }

    .megamenu__list {
        display: flex;
        gap: 0;
    }

    .megamenu__panel {
        position: absolute;
        top: 100%;
        left: 0;
        width: 100vw;
        max-width: 1200px;
        transform: none;
        display: none;
        background: #fff;
        box-shadow: 0 8px 24px rgba(0,0,0,.1);
        border-top: 2px solid var(--color-primary);
    }

    .megamenu__item:hover .megamenu__panel,
    .megamenu__item:focus-within .megamenu__panel {
        display: block;
    }
}

JavaScript: управління drawer і слайдами

class MegaMenu {
    constructor(nav) {
        this.nav     = nav;
        this.toggle  = document.querySelector('.menu-toggle');
        this.panels  = nav.querySelectorAll('.megamenu__panel');
        this.stack   = []; // історія відкритих панелей

        this.bindEvents();
    }

    bindEvents() {
        this.toggle?.addEventListener('click', () => this.open());

        this.nav.querySelector('.megamenu__close')
            ?.addEventListener('click', () => this.close());

        this.nav.querySelector('.megamenu__backdrop')
            ?.addEventListener('click', () => this.close());

        // Перехід до підпанелі
        this.nav.querySelectorAll('.has-children').forEach(link => {
            link.addEventListener('click', e => {
                if (window.innerWidth < 1024) {
                    e.preventDefault();
                    this.goTo(link.dataset.panel);
                }
            });
        });

        // Кнопка «Назад»
        this.nav.querySelectorAll('.megamenu__back').forEach(btn => {
            btn.addEventListener('click', () => this.goBack());
        });

        // Закриття по Escape
        document.addEventListener('keydown', e => {
            if (e.key === 'Escape') this.close();
        });
    }

    open() {
        this.nav.classList.add('is-open');
        this.nav.setAttribute('aria-hidden', 'false');
        this.toggle?.setAttribute('aria-expanded', 'true');
        document.body.style.overflow = 'hidden';
    }

    close() {
        this.nav.classList.remove('is-open');
        this.nav.setAttribute('aria-hidden', 'true');
        this.toggle?.setAttribute('aria-expanded', 'false');
        document.body.style.overflow = '';
        // Скидання стану панелей
        setTimeout(() => this.resetPanels(), 300);
    }

    goTo(panelId) {
        const root    = this.nav.querySelector('.megamenu__panel--root');
        const target  = this.nav.querySelector(`#panel-cat-${panelId}`);
        if (!target) return;

        root.classList.add('slide-out');
        target.classList.add('is-active');
        this.stack.push(panelId);
    }

    goBack() {
        this.stack.pop();
        const root = this.nav.querySelector('.megamenu__panel--root');

        this.panels.forEach(p => {
            if (!p.classList.contains('megamenu__panel--root')) {
                p.classList.remove('is-active');
            }
        });

        root.classList.remove('slide-out');

        if (this.stack.length > 0) {
            this.goTo(this.stack[this.stack.length - 1]);
        }
    }

    resetPanels() {
        this.stack = [];
        const root = this.nav.querySelector('.megamenu__panel--root');
        root.classList.remove('slide-out');
        this.panels.forEach(p => {
            if (!p.classList.contains('megamenu__panel--root')) {
                p.classList.remove('is-active');
            }
        });
    }
}

document.addEventListener('DOMContentLoaded', () => {
    const nav = document.getElementById('site-nav');
    if (nav) new MegaMenu(nav);
});

Продуктивність на мобільних

  • Зображення підкатегорій: loading="lazy", WebP через <picture>, размер 40×40
  • Анімації тільки через CSS transform и opacity — GPU, без reflow
  • overscroll-behavior: contain — панель не прокручує body
  • height: 100dvh — коректна висота з урахуванням адресного рядка браузера

Терміни реалізації

Конфігурація Термін
Drawer + slide-навігація (мобільний) 3–4 дні
+ єдиний шаблон для мобайл/десктоп 5–7 днів
+ анімації, жести свайпу, доступність +2–3 дні