свое контекстное меню javascript
Контекстное меню на javascript: небольшое, но мощное
UPD: разместил проект в google code, пользуйтесь, развивайте:
svn checkout js-cmenu.googlecode.com/svn/trunk js-cmenu-read-only
Функционал
Подменю есть. Их вложенность теоретически не ограничена.
Пункты меню можно делать недоступными (disabled=true), невидимыми (visible=false), можно динамически изменять caption, icon и добавлять новые пункты меню и подменю.
Корректно работает у различных границ областей экрана, отрабатывается ситуация когда меню находится в скроллируемом диве (скролл вместе с элементом, вызвавшим меню).
Радиоменю: выбор одного из пунктов меню.
Несколько вариантов построения и дальнейшего поведения меню.
Из соображений отсутствия необходимости следующие функции были изъяты: создание меню по ajax-запросу, вызов по правой кнопке (не везде работает), горизонтальное меню (крайне редко использовалось).
Как это работает
Есть глобальная коллекция, куда собираются все-все меню (линейный список). Собственно меню представляет собой некий объект, содержащий информацию о своем поведении и состоянии, а также пункты меню. Пункт меню может содержать подменю.
Для отображения меню на странице необходимо указать на какой-либо элемент страницы (я обычно использую картинки или ссылки), а также указать событие данного элемента по которому меню будет вызвано, если оно (событие) отлично от клика.
Одно меню можно фигачить во многие элементы на странице. От этого меню не размножится, но тем не менее будет знать, откуда его вызвали (это ведь принципиально в обработчике).
Я старался сделать код максимально простым и понятным, чтобы добавление новых функций и удаление чего-то ненужного не представляло трудностей. К примеру, реализация радио-меню потребовала пять или шесть строчек и 10 минут времени. Модифицируйте код как хотите, но в комментах указывайте всё же ссылку автора.
Примеры создания и вызова
Это всё покроет основную массу задач. Но что, если нам надо меню, динамически меняющееся в зависимости от внешних факторов? Я много думал об этом, пробовал различные реализации, в итоге устоялась одна: передаем конструктору меню функцию. Эта функция будет вызываться всякий раз, когда надо показать меню.
Внимание: важно!
Для оптимизации работы меню в целом эта функция работает довольно странно. Она получает в качестве единственного параметра объект-меню в полное распоряжение. Вернуть она должна либо ложь (это будет означать, что меню не требует перерисовки), либо истину, либо массив действий. Но массив действий можно не возвращать, а просто записать его в член «a» объекта-меню — menu.a = [массив действий], это равносильно.
Часто меню зависит не только от состояния окружения, но и от того, какой элемент меню вызвал. Для этого меню имеет член caller. Он содержит ссылку на DOM-элемент, вызвавший меню. Для подменю этот элемент будет ссылкой на дом-элемент родительского меню, поэтому имеет смысл смотреть на член parentMenu, содержащий ссылку на меню-родителя.
Типичная функция выглядит так:
Пару слов про объект-action. Самый важный метод в нем — это execute. Этот метод будет вызван при клике на пункте меню. Он принимает три параметра. Первый — сам объект-действие, второй — меню, третий — массив-цепочка вызовов меню (для сложных многоуровневых меню может пригодиться).
Менее важный член объекта-действия — submenu. Тут может быть массив действий, либо функция-генератор меню.
Для радио-поведения меню задайте в объекте меню свойство menu.type = ‘radio’, и два метода: set(str) и get
Если знаете реализации лучше, пожалуйста, не скрывайте — высказывайтесь.
Создаем контекстное меню для сайта на JavaScript

Много различным бесплатных шаблонов вы можете найти на сайте наших партнеров, также имеются и премиум шаблоны с круглосуточной техподдержкой и помощью в настройке:
Такая навигация станет отличным дополнением для дизайнера который хочет визуально улучшить свой сайт при этом добавив новые, интересные возможности. И так, давайте приступим.
Шаг 1. HTML
У нас будет список, который будет содержать по очередности подключенные пункты class=»menu» будет содержать в себе всю навигацию, нам необходим для отображения блока с навигационными пунктами:
Собственное контекстное меню с использованием JavaScript
Что есть контекстное меню?
Если верить Википедии, контекстное меню — меню, появляющееся при взаимодействии пользователя с графическим интерфейсом (при нажатии правой кнопки мыши). Контекстное меню содержит ограниченный набор возможных действий, который обычно связаны с выбранным объектом.
На вашем компьютере клик правой кнопкой мыши на рабочем столе вызовет контекстное меню операционной системы. Отсюда вы, вероятно, можете создать новую папку, получить какую-то информацию и сделать что-нибудь еще. Контекстное меню в браузере позволяет, например, получить информацию о странице, посмотреть ее исходники, сохранить изображение, открыть ссылку в новой вкладке, поработать с буфером обмена и всякое такое. Причем набор доступных действий зависит от того, куда именно вы кликнули, то есть от контекста. Это стандартное поведение, закладываемое разработчиками браузера [И расширений к нему].
Веб-приложения постепенно начинают заменять стандартные контекстные меню своими собственными. Отличными примерами являются все те же Gmail и Dropbox. Вопрос лишь в том, как сделать свое контекстное меню? В браузере при клике правой кнопкой мыши срабатывает событие contextmenu. Нам придется отменить поведение по умолчанию и сделать так, чтобы вместо стандартного меню выводилось наше собственное. Это не так уж сложно, но разбираться будем пошагово, так что выйдет довольно объемно. Для начала создадим базовую структуру приложения, чтоб разрабатываемый пример не был совсем уж оторван от реальности.
Список задач
Представим, что мы создаем приложение, позволяющее вести список задач. Я понимаю, вы уже наверняка неимоверно устали от всех этих списков задач, но пусть будет. Страница приложения содержит список незавершенных задач. Для каждой задачи доступен типичный набор действий CRUD: получить информацию о задаче, добавить новую, редактировать, удалить.
Пример результата есть на CodePen. Можете заглянуть туда сразу, если лень читать или хотите убедиться, что действительно заинтересованы в дальнейшем чтении. Ну а пока приступим к пошаговой разработке задуманного. Я буду использовать некоторые современные фишки CSS и создам простейший список задач на data-атрибутах. Также воспользуюсь сбросом стилей от Эрика Мейера и сброшу свойство box-sizing для всех элементов в border-box:
Я не буду использовать префиксы в CSS, но в демо на CodePen включен автопрефиксер.
Создание базовой структуры
Откроем наш HTML-документ, накидаем шапку, контентную часть с некоторым списком задач и подвал. Я также подтяну Font Awesome и шрифт Roboto, чтобы сделать оформление немного лучше. Каждая задача должна содержать атрибут data-id, который в реальности брался бы из БД. Также каждая задача будет содержать список действий. Вот важные части разметки:
Если вы используете CodePen, можете в настройках включить автопрефиксер и подключение CSS-сброса. В противном случае придется делать все руками, если еще не автоматизировали этот процесс. Не забывайте, что наша цель — создание контекстного меню, так что обработка действий не будет реализована. А теперь давайте добавим еще немного CSS:
Полный набор стилей (да и всего остального) представлен на CodePen. А здесь будут самые важные части кода, разметки и оформления. Но давайте уже наконец приблизимся к нашему контекстному меню.
Набросаем наше контекстное меню — разметка
Приводим наше меню в порядок — CSS
Уже была упомянута необходимость абсолютного позиционирования разрабатываемого меню. Кроме того, установим свойство z-index в 10. Не забывайте, что в вашем приложении может потребоваться другое значение. Это не все возможные стили, в демке наведены прочие красивости, но они уже зависят от ваших потребностей и не являются обязательными. Прежде чем переходить к JS, сделаем меню невидимым по умолчанию и добавим дополнительный класс для его отображения.
Разворачиваем наше контекстное меню — JavaScript
Начнем с того, что посмотрим, как зарегистрировать событие contextmenu. Откроем самовыполняющуюся функцию и отловим событие на всем документе. Также будем логировать событие в консоль, чтобы получить какую-то информацию:
Если заглянуть в консоль, можно увидеть, что уникальное событие срабатывает при каждом нажатии на элемент из списка задач. Теперь помимо отмены поведения по умолчанию реализуем отображение контекстного меню, добавляя к нему вспомогательный класс.
Но для начала давайте добавим к меню ID, чтобы его проще было получать посредством JS. Также добавим переменную состояния меню menuState и и переменную с активным классом. Получились три переменных:
Едем дальше. Пересмотрим функцию contextMenuListener и добавим toggleMenuOn, отображающую меню:
На данный момент правой кнопкой мыши уже можно вызвать наше контекстное меню. Но нельзя сказать, что оно работает правильно. Во-первых, оно находится совсем не там, где хотелось бы. Для устранения проблемы понадобится немного математики. Во-вторых, закрыть это меню пока невозможно. С учетом того, как работают обычные контекстные меню, хотелось бы, чтоб наша его реализация закрывалась при клике не меню и при нажатии Escape. Помимо этого при правом клике вне нашего меню оно должно закрыться, а вместо него требуется открытие меню по умолчанию. Давайте попробуем все это решить.
Рефакторинг нашего кода
Вернемся и отредактируем contextListener:
Имея вспомогательную функцию, делающую за нас часть грязной работы, и отлавливая событие contextmenu на всем документе, мы можем теперь закрывать меню при клике вне его. Для этого добавим функцию toggleMenuOff и отредактируем contextListener:
Теперь нажмите правой кнопкой по элементу списка. А после — где-нибудь в другом месте документа. Вуаля! Наше меню закрылось и открылось стандартное. Затем сделаем нечто похожее самое для события click, чтобы не от одной правой кнопки оно закрывалось:
Этот кусок кода несколько отличается от предыдущего, потому что Firefox. После того, как правая кнопка мыши отжата, в Firefox срабатывает событие click, так что здесь нам приходится дополнительно убеждаться, что произошел действительно клик левой кнопкой. Теперь меню не моргает при правом клике. Давайте добавим похожий обработчик и на нажатие клавиши ESC:
Мы получили меню, которое открывается и закрывается как задумано, взаимодействующее с пользователем естественным образом. Давайте наконец отпозиционируем меню и попробуем обрабатывать события внутри него.
Позиционирование нашего контекстного меню
С учетом текущих HTML и CSS, наше меню отображается в нижней части экрана. Но нам-то хотелось бы, чтоб оно появлялось там, где произошел клик. Давайте исправим сие досадное упущение. Во-первых, добавим еще одну вспомогательную функцию, получающую точные координаты клика. Назовем ее getPosition и попробуем заставить обрабатывать разные причуды браузеров:
Наш первый шаг в позиционировании меню — подготовка трех переменных. Добавим их в соответствующий блок кода:
Создадим функцию positionMenu, принимающую единственный аргумент — событие. Пока что пусть она выводит координаты меню в консоль:
Отредактируем contextListener, чтобы начать процесс позиционирования:
Снова повтыкайте в контекстное меню и загляните в консоль. Убедитесь, что позиция действительно доступна и логируется. Мы можем использовать встроенные стили для задания свойств top и left посредством JS. Вот и новая версия positionMenu:
Как вы помните, по умолчанию наше меню скрыто, так что нельзя просто взять и посчитать его размеры. В нашем случае меню статическое, но при реальном применении его содержимое может меняться в зависимости от контекста, так что рассчитывать ширину и высоту лучше в момент открытия. Получим нужные величины внутри функции positionMenu:
Введем еще две переменных, но на этот раз для размеров окна:
Посчитаем их значения похожим образом:
В конечном счете давайте предположим, что хотим показывать меню не ближе, чем в 4 пикселях от края окна. Можно сравнить значения, как я говорил выше, и скорректировать позицию как-то так:
Сейчас наше меню ведет себя совсем хорошо. Осталось что-то сделать с ресайзом окна. Я уже говорил, как поступает Dropbox, но вместо этого мы будем закрывать контекстное меню. [Такое поведение куда ближе к стандартному] Добавим в функцию init следующую строку:
И напишем саму функцию:
Цепляем события к пунктам контекстного меню
Если ваше приложение сложнее данного примера, и у вас планируется динамическое содержимое контекстного меню, придется подробнее вникнуть в происходящее далее, чтобы самому додумать недостающие детали. В нашем приложении все проще, и есть всего одно меню с постоянным набором действий. Таким образом, можно быстро проверить, какой именно элемент был выбран, и обработать этот выбор. В нашем примере просто сохраним выбранный элемент в переменную и запишем в консоль его data-id и выбранное действие. Для этого отредактируем разметку меню:
Далее давайте закэшируем все нужные объекты:
Появилась переменная taskItemInContext, которой присваивается значение при правом клике по элементу списка. Она нам потребуется для логирования ID элементов. Также появились новые имена классов. Теперь пройдемся по функциональности.
Функция инициализации остается такой же. Первое изменение затрагивает contextListener, ведь мы хотим сохранять в taskItemInContext элемент, на который кликнул пользователь, а функция clickInsideElement как раз его и возвращает:
Мы сбрасываем ее в null, если правый клик произошел не по элементу списка. Ну и возьмемся за clickListener. Как я упоминал ранее, для простоты мы просто будем выводить информацию в консоль. Сейчас при отлавливании события click производится несколько проверок и меню закрывается. Внесем коррективы и начнем обрабатывать клик внутри контекстного меню, выполняя некоторое действие и лишь после этого закрывая меню:
Вы могли заметить, что вызывается функция menuItemListener. Ее мы определим чуть позже. Функции keyupListener, resizeListener и positionMenu оставляем без изменений. Функции toggleMenuOn и toggleMenuOff отредактируем незначительно, изменив имена переменных для лучшей читаемости кода:
Наконец реализуем menuItemListener:
На этом разработка функциональности заканчивается.
Некоторые замечания
Большой вопрос
Я выделил эту проблему в отдельный пункт, потому что это действительно важно после всего, что мы проделали. Задайтесь вопросом: действительно ли вам нужно собственное контекстное меню? Такие штуки — это круто, но прежде чем их использовать, вы должны убедиться, что это действительно полезно в вашем случае. Обычно пользователи ожидают привычного поведения приложения. Например, после правого клика по фотографии они ожидают получения возможности сохранить ее, скопировать ссылку и т. д. Отсутствие нужных пунктов в кастомном меню может их огорчить.
Совместимость с браузерами
Заключение и демо
Если вы тщательно все обдумали и уверены, что такая функциональность нужна вашему приложению, вы можете использовать разработанное меню. Конечно, оно может потребовать каких-либо изменений, но данное руководство подробно описывает процесс разработки, так что реализовывать свои поправки должно быть не так сложно.
Кодовая база этого руководства залита на GitHub и будет поддерживаться и, возможно, обновляться в течение долгого времени. А полное рабочее демо можно потыкать на CodePen.
От переводчика
Перевод местами достаточно вольный, но не в ущерб смыслу или содержанию. Все, что не относится напрямую к оригиналу, вынесено в примечания.
С предложениями, пожеланиями и замечаниями, как обычно, в ЛС.
Пишем свое контекстное меню на jQuery
Доброго времени суток.
Сегодня я хочу поделиться опытом написания своего контекстного меню с помощью всеми нами любимой библиотеки jQuery.
Итак, немного предыстории
Что подтолкнуло меня к тому, что я заинтересовался вопросом написания своего контекстного меню и решил написать эту статью? А то, что я являюсь большим фанатом продуктов Google и мне всегда очень нравился подход этой компании к интерфейсам пользователя.
Ежедневно пользуясь его продуктами, такими как Google Drive, меня всегда поражало, до чего же удобно выполнять различные операции с помощью знакомого с детства контекстного меню, просто кликая на объекте правой кнопкой мыши и выбирая необходимое действие. Этот факт и заставил меня заинтересоваться, а насколько сложно самостоятельно реализовать вызов своего контекстного меню в браузере, по клику на любой объект, а так же запретить вызов стандартного меню браузера?
Я решил попробовать и вот что у меня получилось.
К делу
Естественно первое с чего нужно начать, это подключить библиотеку jQuery:
Далее нужно запретить вызов стандартного контекстного меню браузера. Для этого нужно добавить слушатель на стандартное событие документа oncontextmenu и просто предотвратить дальнейшее выполнение стандартных действий вернув false:
Заранее напишем немного css:
Теперь все готово и мы можем начинать творить чудеса jQuery:
Далее все что нужно сделать, это добавить несколько любых HTML элементов на страницу для тестирования.
Спасибо всем за внимание. Надеюсь кому то пригодиться этот рецепт.
How to add a custom right-click menu to a webpage?
I want to add a custom right-click menu to my web application. Can this be done without using any pre-built libraries? If so, how to display a simple custom right-click menu which does not use a 3rd party JavaScript library?
I’m aiming for something like what Google Docs does. It lets users right-click and show the users their own menu.
NOTE: I want to learn how to make my own versus using something somebody made already since most of the time, those 3rd party libraries are bloated with features whereas I only want features that I need so I want it to be completely hand-made by me.
20 Answers 20
Was very useful for me. For the sake of people like me, expecting the drawing of menu, I put here the code I used to make the right-click menu:
A combination of some nice CSS and some non-standard html tags with no external libraries can give a nice result (JSFiddle)
HTML
Note: the menu tag does not exist, I’m making it up (you can use anything)
The JavaScript is just for this example, I personally remove it for persistent menus on windows
Also note, you can potentially modify menu > menu
According to the answers here and on other ‘flows, I’ve made a version that looks like the one of Google Chrome, with css3 transition. JS Fiddle
Lets start eazy, since we have the js above on this page, we can worry about the css and layout. The layout that we will be using is an element with a element or a font awesome icon ( ) and a to show the keyboard shortcuts. So this is the structure:
We will put these in a div and show that div on the right-click. Let’s style them like in Google Chrome, shall we?
And that is it! Just add the css transisions to fade in and out, and done!





