Олег Марков
Контекстное меню в HTML - menu
Введение
Контекстное меню в веб-приложении — это меню, которое появляется при определенном действии пользователя, чаще всего при клике правой кнопкой мыши. В нативных приложениях вы привыкли к этому поведению, и пользователи часто ожидают такого же опыта в браузере.
В HTML когда-то пытались стандартизировать контекстное меню через тег <menu> и атрибут contextmenu. Теоретически вы могли написать HTML-разметку меню, привязать ее к элементу, и при правом клике показывалось бы собственное контекстное меню вместо системного. Но на практике все оказалось гораздо сложнее.
В этой статье мы разберем:
- зачем вообще нужен HTML-тег
<menu>и что с ним сейчас; - почему старый механизм контекстного меню через
contextmenuсчитается устаревшим; - как сегодня корректно реализовывать контекстное меню на HTML, CSS и JavaScript;
- какие есть нюансы UX и доступности (accessibility);
- как аккуратно управлять нативным меню браузера и событиями мыши.
Смотрите, я буду показывать вам примеры кода и по шагам объяснять, как собрать своё контекстное меню, не опираясь на устаревшие и плохо поддерживаемые возможности.
История и статус тега <menu> и атрибута contextmenu
Что такое <menu> в HTML
Тег <menu> задумывался как семантический контейнер для набора команд или пунктов меню. Идея была в том, чтобы можно было описать меню в HTML, а браузер уже знал бы, что это именно меню команд, а не просто список.
Простейший пример выглядел так:
<!-- Устаревший пример использования HTML-контекстного меню -->
<div id="target" contextmenu="my-menu">
Кликните правой кнопкой по этому блоку
</div>
<menu type="context" id="my-menu">
<!-- Пункт контекстного меню -->
<menuitem label="Обновить" icon="refresh.png"></menuitem>
<menuitem label="Удалить"></menuitem>
</menu>Комментарии к примеру:
- атрибут
contextmenu="my-menu"на<div>указывает браузеру, что при правом клике нужно показать меню сid="my-menu"; - тег
<menu type="context">объявлял контекстное меню; - тег
<menuitem>должен был представлять отдельный пункт меню.
На бумаге это выглядело логично и удобно. Но:
- поддержка в браузерах была фрагментарной (прежде всего в старых версиях Firefox);
- поведение отличалось от браузера к браузеру;
- в спецификациях HTML5 часть связанного функционала признали неудачной.
Текущий статус <menu> и menuitem
Важно понимать текущее состояние стандарта, чтобы вы не строили архитектуру на том, что уже признано устаревшим:
<menu type="context">и<menuitem>устарели (obsolete) и больше не развиваются;- большинство современных браузеров не поддерживают или частично игнорируют атрибут
contextmenuи<menuitem>; - некоторые браузеры продолжают понимать
<menu>как обычный блочный элемент или список, но без «магии» контекстного меню.
То есть рассчитывать на нативную реализацию контекстных меню через HTML-разметку сегодня нельзя. Если вы пытаетесь использовать <menu> с type="context" и ждете появления встроенного меню по правому клику — в актуальных браузерах этого не произойдет.
Когда <menu> еще имеет смысл
Тем не менее тег <menu> полностью не исчез:
- его всё еще можно встретить в старых HTML-страницах;
- иногда его используют как семантический контейнер для групп кнопок, например панелей инструментов.
Но в современном коде вместо <menu> чаще используют:
<nav>— для навигационных меню;<ul><li>— как базовую структуру для любых списков пунктов меню;
<div>/<button>— для кастомных UI-компонентов.
Если вы разрабатываете новое приложение, лучше не полагаться на <menu> как на механизм контекстных меню. Давайте теперь посмотрим, как реализовать контекстное меню современным способом.
Современный подход: контекстное меню на JS и CSS
Общая идея реализации
Так как HTML-механизм через <menu> и contextmenu фактически не работает, сегодня контекстные меню делают следующим образом:
- Отслеживают событие
contextmenuна нужном элементе (или наdocument). - Отменяют стандартное контекстное меню браузера.
- Показывают свой HTML-блок с пунктами меню в координатах клика.
- Обрабатывают нажатия по пунктам меню с помощью JavaScript.
- Прячут меню при клике вне его, скролле, нажатии Escape и т.д.
Структурно это выглядит несложно, но есть нюансы по позиционированию, доступности и работе с событиями.
Давайте разберем базовую реализацию.
Базовая разметка контекстного меню
Начнем с простой структуры HTML:
<!-- Элемент, по которому будет вызываться контекстное меню -->
<div id="context-target" class="context-target">
Кликните правой кнопкой по этому блоку
</div>
<!-- Наше кастомное контекстное меню -->
<div id="custom-menu" class="custom-menu" aria-hidden="true">
<button type="button" class="menu-item" data-action="refresh">
Обновить
</button>
<button type="button" class="menu-item" data-action="edit">
Редактировать
</button>
<button type="button" class="menu-item" data-action="delete">
Удалить
</button>
</div>Обратите внимание:
- используем обычный
<div>для контейнера меню; - каждый пункт — это
<button>сdata-actionдля идентификации действия; - атрибут
aria-hidden="true"помогает скрыть меню от экранных читалок, когда оно не активно (мы будем переключать его при показе).
Стилизация и позиционирование меню
Теперь давайте добавим CSS, чтобы меню выглядело как всплывающее:
/* Целевой блок для вызова контекстного меню */
.context-target {
border: 1px dashed #999; /* Рамка вокруг блока */
padding: 20px; /* Внутренние отступы */
margin: 40px; /* Внешние отступы */
user-select: none; /* Запрещаем выделение текста при клике */
}
/* Базовый стиль меню - скрытое всплывающее окно */
.custom-menu {
position: absolute; /* Позволяет располагать меню по координатам мыши */
min-width: 160px; /* Минимальная ширина меню */
background: #fff; /* Фон меню */
border: 1px solid #ccc; /* Граница для визуального отделения */
border-radius: 4px; /* Небольшое скругление углов */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); /* Тень под меню */
padding: 4px 0; /* Вертикальные отступы */
display: none; /* Изначально меню скрыто */
z-index: 1000; /* Показываем поверх других элементов */
}
/* Отображение меню */
.custom-menu.is-visible {
display: block; /* Показать меню */
}
/* Стиль отдельных пунктов меню */
.custom-menu .menu-item {
display: block; /* Кнопка как блок чтобы занимала всю ширину */
width: 100%;
padding: 6px 12px; /* Внутренние отступы для удобства клика */
text-align: left; /* Выравнивание текста по левому краю */
background: transparent; /* Прозрачный фон по умолчанию */
border: none; /* Убираем стандартную рамку кнопки */
cursor: pointer; /* Курсор в виде руки */
font: inherit; /* Наследуем шрифт от родителя */
}
/* Ховер и фокус для пунктов меню */
.custom-menu .menu-item:hover,
.custom-menu .menu-item:focus {
background: #f0f0f0; /* Подсветка пункта меню */
}Теперь меню выглядит как привычное контекстное меню и может появляться в любой точке страницы.
Логика отображения меню на JS
Теперь вы увидите, как это выглядит в JavaScript. Смотрите, я покажу вам базовый скрипт:
// Находим элементы в DOM
const target = document.getElementById('context-target'); // Элемент, по которому кликаем
const menu = document.getElementById('custom-menu'); // Наше меню
// Функция для показа меню в заданных координатах
function showMenu(x, y) {
// Устанавливаем координаты меню относительно окна
menu.style.left = `${x}px`;
menu.style.top = `${y}px`;
// Добавляем класс видимости
menu.classList.add('is-visible');
menu.setAttribute('aria-hidden', 'false'); // Делаем меню доступным для assistive технологий
}
// Функция для скрытия меню
function hideMenu() {
menu.classList.remove('is-visible');
menu.setAttribute('aria-hidden', 'true'); // Скрываем меню для assistive технологий
}
// Обработчик события контекстного меню (правый клик)
target.addEventListener('contextmenu', function (event) {
event.preventDefault(); // Отключаем штатное контекстное меню браузера
// Показываем наше меню в месте клика
showMenu(event.clientX, event.clientY);
});
// Скрываем меню при клике вне его
document.addEventListener('click', function (event) {
// Проверяем, что клик был не по самому меню и не по его потомкам
if (!menu.contains(event.target)) {
hideMenu();
}
});Комментарии к логике:
- событие
contextmenuвозникает при правом клике — мы отменяем стандартное поведение и показываем своё меню; - координаты берем из
event.clientXиevent.clientY, чтобы привязать меню к месту клика; - любое нажатие мышью вне меню закрывает его.
На этом этапе меню уже работает, но есть важные детали, которые пока не учтены: границы окна, клавиатура, мобильные устройства.
Корректное позиционирование в пределах окна
Проблема выхода меню за край экрана
Если пользователь кликнет правой кнопкой у правого или нижнего края окна, меню может частично выйти за пределы видимой области. Это неудобно: часть пунктов будет недоступна.
Давайте разберемся на примере, как это решить.
Идея:
- Сначала показываем меню «временно», чтобы браузер рассчитался его размер.
- Берем ширину и высоту меню (
offsetWidth,offsetHeight). - Сравниваем координаты клика плюс размеры меню с размером окна (
window.innerWidth,window.innerHeight). - При необходимости сдвигаем меню влево или вверх.
Реализация безопасного позиционирования
Покажу вам, как это реализовано на практике:
function showMenuSafely(clickX, clickY) {
// Временно показываем меню вне экрана чтобы узнать его размеры
menu.style.visibility = 'hidden'; // Делаем невидимым
menu.style.display = 'block'; // Но при этом отображаем в потоке
menu.style.left = '0px';
menu.style.top = '0px';
const menuWidth = menu.offsetWidth; // Ширина меню
const menuHeight = menu.offsetHeight; // Высота меню
const windowWidth = window.innerWidth; // Ширина окна
const windowHeight = window.innerHeight; // Высота окна
// Начальные координаты - координаты клика
let x = clickX;
let y = clickY;
// Если меню целиком не помещается по горизонтали - сдвигаем влево
if (x + menuWidth > windowWidth) {
x = windowWidth - menuWidth - 4; // Небольшой отступ от края
}
// Если меню целиком не помещается по вертикали - сдвигаем вверх
if (y + menuHeight > windowHeight) {
y = windowHeight - menuHeight - 4; // Небольшой отступ от края
}
// Возвращаем нормальную видимость и устанавливаем окончательные координаты
menu.style.left = `${x}px`;
menu.style.top = `${y}px`;
menu.style.visibility = 'visible';
// Добавляем класс видимости
menu.classList.add('is-visible');
menu.setAttribute('aria-hidden', 'false');
}
// Используем новую функцию в обработчике
target.addEventListener('contextmenu', function (event) {
event.preventDefault(); // Отключаем системное контекстное меню
showMenuSafely(event.clientX, event.clientY); // Показываем меню с учетом границ окна
});Так мы гарантируем, что меню не «улетит» за край и останется полностью видимым.
Обработка действий меню и работа с data-*
Привязка логики к пунктам меню
Само появление меню — это только половина задачи. Самое главное — обрабатывать действия пользователя. Здесь удобно использовать data-* атрибуты.
Давайте посмотрим, что происходит в следующем примере:
// Обрабатываем клики по пунктам меню
menu.addEventListener('click', function (event) {
const item = event.target.closest('.menu-item'); // Ищем ближайший пункт меню
if (!item) return; // Если клик не по пункту меню - игнорируем
const action = item.dataset.action; // Читаем значение data-action
handleMenuAction(action); // Вызываем обработчик действий
// После выбора пункта меню - скрываем его
hideMenu();
});
// Функция обработки действий меню
function handleMenuAction(action) {
// Здесь вы можете реализовать любую логику
// Для примера выведем действие в консоль и alert
switch (action) {
case 'refresh':
console.log('Выбрано действие - обновить');
alert('Обновление содержимого');
// Здесь могла бы быть логика перезагрузки данных
break;
case 'edit':
console.log('Выбрано действие - редактировать');
alert('Переход в режим редактирования');
break;
case 'delete':
console.log('Выбрано действие - удалить');
if (confirm('Вы уверены что хотите удалить?')) {
alert('Удалено');
// Здесь могла бы быть логика удаления элемента
}
break;
default:
console.warn('Неизвестное действие меню', action);
}
}Такой подход удобен потому, что:
- структура меню лежит в HTML;
- логика — в одном обработчике
handleMenuAction; - вы легко добавляете новые пункты — просто добавляете кнопку с нужным
data-action.
Контекст: по какому элементу кликнули
Часто важно знать, по какому именно элементу пользователь вызвал контекстное меню. Например, вы показываете список файлов, и меню должно работать для конкретного файла.
Здесь я размещаю пример, чтобы вам было проще понять.
Пример: контекстное меню для списка элементов
Разметка:
<ul id="file-list">
<li class="file-item" data-id="1">Файл 1.txt</li>
<li class="file-item" data-id="2">Файл 2.txt</li>
<li class="file-item" data-id="3">Файл 3.txt</li>
</ul>
<div id="file-menu" class="custom-menu" aria-hidden="true">
<button type="button" class="menu-item" data-action="open">Открыть</button>
<button type="button" class="menu-item" data-action="rename">Переименовать</button>
<button type="button" class="menu-item" data-action="delete">Удалить</button>
</div>JavaScript:
const fileList = document.getElementById('file-list'); // Список файлов
const fileMenu = document.getElementById('file-menu'); // Меню для файлов
let currentFileId = null; // Идентификатор файла с которым работаем
// Обработчик контекстного меню на списке файлов
fileList.addEventListener('contextmenu', function (event) {
const item = event.target.closest('.file-item'); // Ищем кликнутый элемент списка
if (!item) return; // Если правый клик не по файлу - игнорируем
event.preventDefault(); // Отключаем стандартное меню
currentFileId = item.dataset.id; // Сохраняем id файла для дальнейших действий
// Показываем контекстное меню у точки клика
showMenuSafely(event.clientX, event.clientY); // Можно переиспользовать ранее написанную функцию
});
// Действия меню зависят от currentFileId
fileMenu.addEventListener('click', function (event) {
const actionItem = event.target.closest('.menu-item');
if (!actionItem) return;
const action = actionItem.dataset.action;
switch (action) {
case 'open':
console.log('Открыть файл с id', currentFileId);
break;
case 'rename':
console.log('Переименовать файл с id', currentFileId);
break;
case 'delete':
console.log('Удалить файл с id', currentFileId);
break;
}
hideMenu();
});Как видите, этот код выполняет привязку контекстного меню к конкретным элементам, используя data-id и сохранение currentFileId.
Поддержка клавиатуры и доступность (A11y)
Почему это важно
Даже если вы не делаете «официально» доступное приложение, поддержка клавиатуры:
- облегчает жизнь продвинутым пользователям (меньше мышки);
- улучшает UX в целом;
- помогает при тестировании и автоматизации.
Для контекстного меню логично:
- открыть меню по клавише (например, Shift+F10 или клавиша контекстного меню);
- позволить перемещаться по пунктам стрелками;
- закрывать меню по Esc.
Открытие меню с клавиатуры
Например, добавим открытие меню по клавише контекстного меню (обычно это ContextMenu) или Shift+F10:
target.addEventListener('keydown', function (event) {
// Проверяем нажатие клавиши контекстного меню или комбинации Shift+F10
const isContextKey = event.key === 'ContextMenu';
const isShiftF10 = event.key === 'F10' && event.shiftKey;
if (!isContextKey && !isShiftF10) {
return; // Игнорируем другие клавиши
}
event.preventDefault();
// Получаем прямоугольник элемента чтобы расположить меню около него
const rect = target.getBoundingClientRect();
// Показываем меню у верхнего левого угла элемента
showMenuSafely(rect.left, rect.top);
});Так пользователь, находясь фокусом на элементе, сможет вызвать контекстное меню без мыши.
Навигация по пунктам меню с клавиатуры
Теперь давайте перейдем к следующему шагу — сделаем навигацию стрелками:
// Возвращает список всех пунктов меню
function getMenuItems() {
return Array.from(menu.querySelectorAll('.menu-item'));
}
// Перемещает фокус на следующий или предыдущий пункт меню
function moveFocus(direction) {
const items = getMenuItems(); // Получаем все пункты
if (!items.length) return;
const currentIndex = items.indexOf(document.activeElement); // Текущий сфокусированный элемент
let nextIndex;
if (currentIndex === -1) {
// Если фокуса еще нет - ставим на первый элемент
nextIndex = 0;
} else {
// Перемещаем фокус по кругу
nextIndex = (currentIndex + direction + items.length) % items.length;
}
items[nextIndex].focus(); // Устанавливаем фокус на выбранный пункт
}
// Обработчик клавиатуры на самом меню
menu.addEventListener('keydown', function (event) {
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
moveFocus(1); // Вниз
break;
case 'ArrowUp':
event.preventDefault();
moveFocus(-1); // Вверх
break;
case 'Escape':
event.preventDefault();
hideMenu(); // Закрываем меню по Esc
break;
case 'Enter':
case ' ':
// Имитируем клик по текущему выбранному пункту меню
event.preventDefault();
if (document.activeElement.classList.contains('menu-item')) {
document.activeElement.click();
}
break;
}
});
// При открытии меню ставим фокус на первый элемент
function showMenuAndFocus(x, y) {
showMenuSafely(x, y);
const items = getMenuItems();
if (items.length) {
items[0].focus(); // Фокус на первый пункт
}
}Теперь ваше меню:
- можно открыть с клавиатуры;
- перемещаться по пунктам стрелками;
- выбирать Enter или пробелом;
- закрывать Esc.
Это сильно приближает поведение к нативным контекстным меню.
Мобильные устройства и жесты
Проблема на тач-экранах
На мобильных устройствах нет «правой кнопки мыши» в привычном виде. Вместо этого есть:
- долгое нажатие (long press);
- жесты (tap, double tap и т.п.).
При долгом нажатии браузеры нередко показывают свои собственные контекстные меню (например, для ссылок — «Открыть в новой вкладке» и т.п.). Это может конфликтовать с вашей логикой.
Подход для touch-устройств
Часто делают так:
- Не навязывают контекстное меню на мобильных устройствах вообще, а заменяют его на обычные действия (например, иконки рядом).
- Или реализуют контекстное меню как отдельный UI-элемент (кнопка с тремя точками), а не через long press.
Если вам всё же нужно поддержать long press, это делается через touchstart/touchend и таймер. Но такой подход:
- конфликтует с системными жестами;
- может вызывать неожиданные эффекты.
Поэтому, если вы делаете кроссплатформенное приложение, лучше:
- показывать контекстное меню по правому клику / клавишам на десктопе;
- на мобильных давать доступ к тем же действиям через явные кнопки.
Взаимодействие с нативным контекстным меню браузера
Отключение и включение стандартного меню
Иногда вам может понадобиться:
- отключить стандартное меню в определенной зоне (например, для канвы или карты);
- но при этом не отключать его по всей странице.
Давайте разберемся на примере отключения только на одном элементе:
const canvasArea = document.getElementById('canvas-area'); // Наш "особый" блок
canvasArea.addEventListener('contextmenu', function (event) {
event.preventDefault(); // Отключаем нативное меню только здесь
// Здесь вы можете открыть свое меню или выполнить какое-либо действие
// Например:
console.log('Правый клик по canvas-area');
});Если же вы вешаете обработчик contextmenu на document и в нём делаете preventDefault(), это отключит стандартное меню везде — с этим лучше быть аккуратнее, чтобы не ломать привычное поведение браузера там, где оно нужно.
Комбинированный подход
Иногда удобно:
- показывать своё меню только при правом клике в определенной зоне;
- а в других местах оставлять нативное меню.
Тогда вы просто:
- ставите
preventDefault()только в нужных местах; - нигде больше не трогаете событие
contextmenu.
Так вы не мешаете пользователю копировать текст, открывать ссылки в новой вкладке и т.д.
Стилизация и UX детали
Поведение при скролле
Когда меню открыто и пользователь скроллит страницу, меню может «уехать» в неожиданное место. Чаще всего в этом случае меню просто скрывают при событии scroll:
// Скрываем меню при скролле окна
window.addEventListener('scroll', hideMenu, true);
// Параметр true - чтобы ловить скролл на всех вложенных элементах (capturing)Так вы избегаете странных ситуаций, когда меню «зависает» не там, где пользователь ожидает.
Множественные контекстные меню
Если на странице несколько независимых контекстных меню (например, разные для файлов и для фона), вам нужно:
- следить, чтобы одновременно было открыто только одно;
- при открытии одного — закрывать другие.
Обычно это решается глобальной функцией hideAllContextMenus() и регистрацией всех меню в каком-то списке.
Условно:
const allMenus = document.querySelectorAll('.custom-menu'); // Все меню на странице
function hideAllContextMenus() {
allMenus.forEach(menuEl => {
menuEl.classList.remove('is-visible');
menuEl.setAttribute('aria-hidden', 'true');
});
}
// В обработчике показа какого-либо меню
function showSpecificMenu(menuEl, x, y) {
hideAllContextMenus(); // Закрываем все другие меню
// Далее позиционируем и показываем только menuEl
}Так вы избегаете ситуации, когда несколько меню перекрывают друг друга.
Темы и адаптация под дизайн
Контекстное меню — часть визуального языка приложения. Совет:
- используйте те же цвета, шрифты и отступы, что и в других выпадающих списках (dropdown, select, попапы);
- обеспечьте достаточный размер кликабельной области (минимум 32px по высоте) для комфортного выбора пунктов;
- добавьте hover-состояния и чёткий фокус для клавиатурной навигации.
Почему не стоит использовать <menu> и contextmenu сегодня
Суммируя всё сказанное по теме самого HTML-тега <menu>:
<menu type="context">и<menuitem>устарели и не поддерживаются современными браузерами на должном уровне;- атрибут
contextmenuфактически бесполезен в современных проектах; - поведение сильно различается от браузера к браузеру и от версии к версии.
Поэтому современные руководства, в том числе MDN, рекомендуют:
- не использовать
<menu>и<menuitem>для контекстных меню; - реализовывать меню с помощью обычной HTML-разметки (
div,ul/li,button) и JavaScript-событий.
При этом вы:
- получаете полный контроль над поведением;
- можете делать продвинутую логику (зависимость от контекста, динамическое содержание меню);
- обеспечиваете одинаковую работу в разных браузерах.
В редких случаях <menu> можно использовать как семантический контейнер для группы команд, но и здесь более распространенные варианты — <nav>, <ul> или <div> с правильными role и aria-* атрибутами.
Всё, что вы можете были бы сделать с <menu>, сегодня надежнее и гибче делается на комбинации HTML+CSS+JS.
Частозадаваемые технические вопросы и ответы
Как сделать так, чтобы контекстное меню работало и по левому клику
Иногда хотят иметь одинаковое меню по правому и левому клику. Тогда просто вешаете обработчик на click и вызываете ту же логику показа:
target.addEventListener('click', function (event) {
// При обычном клике показываем то же меню
showMenuSafely(event.clientX, event.clientY);
});Важно не забывать, что такое поведение может быть непривычным, поэтому лучше явно обозначать элементы, по которым открывается меню (например, иконка «три точки»).
Как динамически изменять содержимое контекстного меню перед показом
Иногда нужно подстраивать пункты меню под конкретный элемент. Подход:
- В обработчике
contextmenuсохраните контекст (например,currentFileId). - Перед вызовом
showMenuSafelyизмените DOM внутри меню:
target.addEventListener('contextmenu', function (event) {
event.preventDefault();
const isSpecial = event.target.dataset.type === 'special';
const deleteBtn = menu.querySelector('[data-action="delete"]');
if (isSpecial) {
deleteBtn.disabled = true; // Запрещаем удаление
} else {
deleteBtn.disabled = false;
}
showMenuSafely(event.clientX, event.clientY);
});Как предотвратить выделение текста при вызове контекстного меню
При правом клике или drag-нажатии текст может выделяться. Чтобы этого избежать:
- используйте CSS-свойство
user-select: none;на нужных элементах; - или в обработчиках
mousedownотменяйте выделение.
.context-target {
user-select: none; /* Запрещаем выделение текста в этом блоке */
}Как сделать вложенные подменю (submenu)
Для вложенных меню структура обычно такая:
- основной пункт с иконкой «»;
- вложенный
<div>с подпунктами, который показывается по hover или фокусу.
Ключевые моменты:
- используйте
position: absoluteдля подменю относительно родительского пункта; - открывайте подменю при наведении и по стрелке вправо с клавиатуры;
- не забывайте закрывать подменю при уходе фокуса.
Реализация сложнее базового примера, поэтому обычно имеет смысл использовать готовые UI-библиотеки или общий компонент «Dropdown + Submenu».
Можно ли использовать один компонент контекстного меню для разных типов элементов
Да, это хороший подход. Схема:
- Делаете один HTML-шаблон меню.
- В
contextmenuобработчике сохраняете тип элемента и его id. - Перед показом меню настраиваете видимость/доступность пунктов.
let context = { type: null, id: null };
document.addEventListener('contextmenu', function (event) {
const user = event.target.closest('.user-item');
const file = event.target.closest('.file-item');
if (user) {
context = { type: 'user', id: user.dataset.id };
// Настраиваем пункты меню именно для пользователя
} else if (file) {
context = { type: 'file', id: file.dataset.id };
// Настраиваем пункты меню именно для файла
} else {
return;
}
event.preventDefault();
showMenuSafely(event.clientX, event.clientY);
});Дальше в обработчике действий вы смотрите на context.type и выполняете соответствующую логику.
Постройте личный план изучения Html до уровня Middle — бесплатно!
Html — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Все гайды по Html
Лучшие курсы по теме

HTML и CSS
Антон Ларичев
TypeScript с нуля
Антон Ларичев