Типичная проблема адаптивного меню: на десктопе подменю раскрывается по наведению, а на телефоне наведения нет. В итоге тап по родительскому пункту сразу уводит на страницу раздела, и до вложенных ссылок пользователь не добирается. Задача – на мобильных перехватить первый тап по пункту с подменю, чтобы он раскрывал список, а не выполнял переход.
Почему старое решение на jQuery устарело
Раньше это делали через $(document).delegate('a', 'click', ...) и проверку видимости бургер-кнопки. Приём рабочий, но у него три слабых места: зависимость от jQuery, определение «мобильности» по видимости элемента вместо реального размера экрана и полное отсутствие доступности – скринридер не понимает, что пункт стал кнопкой-раскрывашкой. Сегодня всё это решается на чистом JavaScript.
Определяем мобильный режим правильно
Вместо проверки :visible используйте window.matchMedia() – он опрашивает ту же контрольную точку, что и CSS, и не зависит от вёрстки:
const mobile = window.matchMedia("(max-width: 992px)");
Перехватываем клик на чистом JS
Навешиваем один обработчик на контейнер меню (делегирование событий) и отменяем переход только для пунктов, у которых есть подменю, и только в мобильном режиме:
const menu = document.querySelector("#main-menu");
menu.addEventListener("click", (e) => {
const link = e.target.closest("a.has-submenu");
if (!link) return;
if (mobile.matches) {
e.preventDefault(); // отменяем переход
const item = link.parentElement;
const expanded = item.classList.toggle("is-open");
link.setAttribute("aria-expanded", expanded);
}
});
Обратите внимание: отмену перехода получают только пункты с классом has-submenu. Обычные ссылки без вложенного меню продолжают работать как ссылки – это важно, иначе на мобильных перестанет работать вся навигация.
Доступность: не ломайте семантику
Если пункт на мобильных перестаёт быть ссылкой и становится переключателем, об этом нужно сообщить вспомогательным технологиям:
- Добавляйте
aria-expanded="false"на пункты с подменю и переключайте значение при раскрытии (в коде выше это уже сделано). - Оборачивайте меню в
<nav aria-label="Основное меню">. - Убедитесь, что подменю раскрывается и с клавиатуры: событие
clickсрабатывает и по Enter на ссылке, так что базовый кейс закрыт, но проверьте порядок фокуса. - Не прячьте у родителя реальную ссылку насовсем – оставьте пользователю способ попасть на страницу раздела (например, отдельной ссылкой «Перейти в раздел» внутри раскрытого подменю).
Частые ошибки
- Отключили переход у всех ссылок. Тогда на телефоне не работает вообще ничего. Отменяйте переход только у пунктов с подменю.
- Определяете мобильность по ширине окна в JS-переменной один раз. При повороте экрана значение устаревает.
matchMediaвсегда актуален через.matches. - Нет визуального признака раскрытия. Пользователь не понимает, что пункт раскрылся. Показывайте стрелку/шеврон и меняйте её по классу
is-open.
Тему меню продолжают заметки как вручную добавить подпункт в подменю Битрикса и вывод блоков только на определённых страницах. Если правите ещё и мобильные кнопки, посмотрите как заменить текст кнопки поиска на иконку.