model / context-menu
A nice context menu you can append wherever you like
Requires
- model/assets: ^0.4.0
This package is auto-updated.
Last update: 2026-08-26 14:49:20 UTC
README
A nice context menu you can append wherever you like.
This is the ModEl 4 port of the legacy ContextMenu module. It ships the menu's CSS/JS as an asset
library declared through an AssetsProvider, so model/assets discovers and serves them
automatically — no manual file inclusion needed.
Unlike the v3 module, it has no hard dependency on the legacy FrontEnd module: the helpers it used
to borrow from it (_, getMouseCoords, addClass, removeClass) are now implemented internally
or replaced by their native equivalents. onHtmlChange is still used to re-scan dynamically loaded
HTML, but only if FrontEnd happens to be loaded.
Installation
composer require model/context-menu
Enabling the assets
The library is registered under the name context-menu but is not auto-enabled. Enable it
(typically from a controller's init) wherever a page uses the context menu:
use Model\Assets\Assets; Assets::enable('context-menu');
This adds style.css (in the head) and js.js (in the foot) to the asset render list.
Usage
A menu is a plain object mapping the label of each voice (HTML is allowed) to the callback to run
when it is picked. Attach it to any element with ctxMenu:
document.getElementById('my-element').ctxMenu({ 'Edit': function () { zkPopup({url: 'edit', get: {id: this.getAttribute('data-id')}}); }, 'Delete': function () { if (confirm('Are you sure?')) this.remove(); }, });
Inside the callbacks, this is the element the menu is attached to.
Right-clicking the element then opens the menu at the cursor; it flips itself back over the cursor if it would otherwise overflow the bottom or the right edge of the window.
Since the voices are the keys of an object, two voices cannot share the same label.
Declarative usage
Instead of calling ctxMenu by hand, the menu can be declared directly in the markup, via the
data-context-menu attribute:
<div data-context-menu="{'Edit': function(){ edit(this); }, 'Delete': function(){ del(this); }}"> Right-click me </div>
The attribute is a JavaScript object literal (it contains functions, so it is eval'd rather than
parsed as JSON). It is scanned at DOMContentLoaded and, if the legacy FrontEnd module is
present, on every onHtmlChange — so elements loaded later via ajax get their menu too. Already
processed elements are flagged with data-set-context-menu and skipped. Call checkZkMenu()
yourself if you inject markup without FrontEnd.
Closing the menu
The menu closes on its own when a voice is picked, when Esc is pressed, when the page is scrolled,
and when the mouse is pressed anywhere outside of it. To close it programmatically:
removeContextMenu();
Keyboard
While the menu is open, ↑/↓ move the selection (wrapping from nothing to the last/first voice),
Enter runs the selected voice, and Esc closes the menu.