MooTools ContextMenu Plugin
Written by David Walsh on Wednesday, January 28, 2009
Click here to learn what has changed to make your code framework-compatible.

ContextMenu is a highly customizable, compact context menu script written with CSS, XHTML, and the MooTools javascript framework. ContextMenu allows you to offer stylish, functional context menus on your website.
The XHTML Menu
<ul id="contextmenu"> <li><a href="#edit" class="edit">Edit</a></li> <li class="separator"><a href="#cut" class="cut">Cut</a></li> <li><a href="#copy" class="copy">Copy</a></li> <li><a href="#paste" class="paste">Paste</a></li> <li><a href="#delete" class="delete">Delete</a></li> <li class="separator"><a href="#quit" class="quit">Quit</a></li> </ul>
Use a list of menu items with one link per item. The href attribute is especially important as it must be named the same as the menu’s action, which you’ll see below.
The Sample CSS
/* context menu specific */
#contextmenu { border:1px solid #999; padding:0; background:#eee; width:200px; list-style-type:none; display:none; }
#contextmenu .separator { border-top:1px solid #999; }
#contextmenu li { margin:0; padding:0; }
#contextmenu li a { display:block; padding:5px 10px 5px 35px; width:155px; font-size:12px; text-decoration:none; font-family:tahoma,arial,sans-serif; color:#000; background-position:8px 8px; background-repeat:no-repeat; }
#contextmenu li a:hover { background-color:#ddd; }
#contextmenu li a.disabled { color:#ccc; font-style:italic; }
#contextmenu li a.disabled:hover { background-color:#eee; }
/* context menu items */
#contextmenu li a.edit { background-image:url(edit.png); }
#contextmenu li a.cut { background-image:url(cut.png); }
#contextmenu li a.copy { background-image:url(copy.png); }
#contextmenu li a.paste { background-image:url(paste.png); }
#contextmenu li a.delete { background-image:url(delete.png); }
#contextmenu li a.quit { background-image:url(quit.png); }
Make the CSS look however you’d like. For the purposes of IE6, however, you’ll want to set the link widths. Also note that the menu should be initialized as “display:none”.
The MooTools Javascript
var ContextMenu = new Class({
//implements
Implements: [Options,Events],
//options
options: {
actions: {},
menu: 'contextmenu',
stopEvent: true,
targets: 'body',
trigger: 'contextmenu',
offsets: { x:0, y:0 },
onShow: $empty,
onHide: $empty,
onClick: $empty,
fadeSpeed: 200
},
//initialization
initialize: function(options) {
//set options
this.setOptions(options)
//option diffs menu
this.menu = $(this.options.menu);
this.targets = $$(this.options.targets);
//fx
this.fx = new Fx.Tween(this.menu, { property: 'opacity', duration:this.options.fadeSpeed });
//hide and begin the listener
this.hide().startListener();
//hide the menu
this.menu.setStyles({ 'position':'absolute','top':'-900000px', 'display':'block' });
},
//get things started
startListener: function() {
/* all elements */
this.targets.each(function(el) {
/* show the menu */
el.addEvent(this.options.trigger,function(e) {
//enabled?
if(!this.options.disabled) {
//prevent default, if told to
if(this.options.stopEvent) { e.stop(); }
//record this as the trigger
this.options.element = $(el);
//position the menu
this.menu.setStyles({
top: (e.page.y + this.options.offsets.y),
left: (e.page.x + this.options.offsets.x),
position: 'absolute',
'z-index': '2000'
});
//show the menu
this.show();
}
}.bind(this));
},this);
/* menu items */
this.menu.getElements('a').each(function(item) {
item.addEvent('click',function(e) {
if(!item.hasClass('disabled')) {
this.execute(item.get('href').split('#')[1],$(this.options.element));
this.fireEvent('click',[item,e]);
}
}.bind(this));
},this);
//hide on body click
$(document.body).addEvent('click', function() {
this.hide();
}.bind(this));
},
//show menu
show: function(trigger) {
//this.menu.fade('in');
this.fx.start(1);
this.fireEvent('show');
this.shown = true;
return this;
},
//hide the menu
hide: function(trigger) {
if(this.shown)
{
this.fx.start(0);
//this.menu.fade('out');
this.fireEvent('hide');
this.shown = false;
}
return this;
},
//disable an item
disableItem: function(item) {
this.menu.getElements('a[href$=' + item + ']').addClass('disabled');
return this;
},
//enable an item
enableItem: function(item) {
this.menu.getElements('a[href$=' + item + ']').removeClass('disabled');
return this;
},
//diable the entire menu
disable: function() {
this.options.disabled = true;
return this;
},
//enable the entire menu
enable: function() {
this.options.disabled = false;
return this;
},
//execute an action
execute: function(action,element) {
if(this.options.actions[action]) {
this.options.actions[action](element,this);
}
return this;
}
});
The ContextMenu plugin offers numerous options:
- actions: a collection of actions (functions) to be executed when a corresponding menu item is clicked
- menu: the ID of the element that represents the menu XHTML
- stopEvent: do you want the element’s default action to be stopped when the menu is triggered to display? (defaults to true)
- targets: element(s) that should show the menu when triggered (defaults to the document body)
- trigger: event that triggers the menu to display (defaults to “contextmenu”, or right-click)
- offsets: an {x,y} object with corresponding x and y offsets (x and y both default to 0)
- onShow: a function to execute when the menu is shown
- onHide: a function to execute when the menu is hidden
- onClick: a function to execute when a menu item is clicked
Beyond these initial options, the ContextMenu class also provide some useful methods:
- disable: disables the context menu
- enable: enables the context menu
- disableItem: disables a given menu item
- enableItem: enables a given menu item
window.addEvent('domready', function() {
//create a context menu
var context = new ContextMenu({
targets: 'a', //menu only available on links
menu: 'contextmenu',
actions: {
copy: function(element,ref) { //copy action changes the element's color to green and disables the menu
element.setStyle('color','#090');
ref.disable();
}
},
offsets: { x:2, y:2 }
});
//sample usages of the enable/disable functionality
$('enable').addEvent('click',function(e) { e.stop(); context.enable(); });
$('disable').addEvent('click',function(e) { e.stop(); context.disable(); });
$('enable-copy').addEvent('click',function(e) { e.stop(); context.enableItem('copy'); });
$('disable-copy').addEvent('click',function(e) { e.stop(); context.disableItem('copy'); });
});
The most dynamic part of the ContextMenu instance is the actions option, where you define what action should be taken per menu item. The action is passed the element clicked on and the reference to the context menu. My above example defines the copy action. When you click the “Copy” context menu item, I turn the text color green and disable the context menu. You may define one action per menu item.
This is version 1 of ContextMenu. I’d like to implement a few more features in the future, including:
- Multi-level menus
- A core set of actions with corresponding functionality.
- addItem and removeItem methods
Have suggestions for a version 2? Share them!
ContextMenu is inspired by jQuery Context Menu Plugin.
Follow via RSS Epic Discussion
Be Heard!
I want to hear what you have to say! Share your comments and questions below.











David,
Great job. I love how context menus improve usability specially in admin UIs.
Nice plugin! A little note on which license you’re releasing this under would be nice :-) I will concider using this one on some of my admin panels, if the license is right, hehe :-)
There’s a couple of things I’d personally alter, though most of these are simply code style differences.
I like to keep the indentation to a minimum, and I also like to take advantage of the way mootools handles groups of elements.
For example, instead of doing this.targets.each and then running a item.addEvent on that, you could simply run addEvent on this.targets instead. The only difference here would be that you’d refer to this.target instead of item, or set item to this.target. Also, instead of doing “if(!this.options.disabled) { indented code below }”, you could check it the other way around, and return.. so it becomes “if(this.options.disabled) return;”
All of these things are, as I said, merely code style, so these are just tips if you want to cut down on the indenting and possibly save a few lines of code. I posted the altered code on your pastebin if you are interested.
@Rexxars: Thank you for the input and I’ll look at the licenses. Hadn’t thought about it. Like everything else on this site, use it however you’d like!
Wow … definitely using this on a few of my sites.
Hi
This is not working in opera… because of opera of course.
As I remember there is a setting in opera to allow or not right click, but most of the users will have the standard settings.
So i think you can detect opera and add something like ctrl+click to launch the menu.
Wow, I’ve been waiting for this for so long!
Unfortunately it doesn’t work on Opera because of how annoying opera is, I am sure you’ll find a solution for this sometime soon, or like rborn suggested! Nonetheless, its a great class!
Thanks :)
@rborn: The beauty is that you can choose the trigger. You could make it double-click if you need Opera users to be able to use the menu.
Excellent, David! I know a lot of places where something like this could be used.
Is it possible to have the menu close when you left click outside of the region?
Updated. Thanks Mark!
It looks fantastic. Looking forward an opportunity to use in one of my projects. Some feedback tho:
– I’m getting errors when running in IE (“Object doesn’t support this property or method” kind of error on lines 144 and 2705);
– Displays fine in FF and Safari. I don’t use Opera in my desktop;
– It didn’t copy the selected text in any browser(Safari and FF) I tested.
keep up the good work :)
@Thiago: It wasn’t supposed to copy text to your clipboard — it’s supposed to turn text green and disable the menu. Also, I’ve just cured the IE issue.
Hi, really nice work !
I used it in a complex interface. It needs to be initialized several times in it’s lifetime. So, when I call for the second time the constructor, the menu will execute two times the expected function…
So, I just removed the events just before add them
At line 64 of ContextMenu Class, I just added this code:
/* menu items */
this.menu.getElements(‘a’).each(function(item){
item.removeEvents();
});
Thank you again for your class :)
Nice script David. I can’t figure out how to keep the script from appending the url when clicking on a menu item. Is there a way??? Thanks in advance.
@David Hinckle: What do you mean by this? I’m not following.
Sorry, I’m probably being too anal about this to begin with. Using your demo as an example, when one clicks on the edit link, “#edit” is appended to the end of the url. I’d like to do away with this. Thanks again. DH
Hey David…I’m having the same problem as @David Hinckle. My script keeps appending “#edit” or whatever the “click” event is to the end of my URL string. Normally this wouldn’t be a problem, but it messes up when I throw .htaccess into the mix.
Example:
Say I’ve got an url: http://www.whatever.com/services/ <== using .htaccess for clean url
When the “click” event is added: http://www.whatever.com/services/#edit <== appended to url after “click” event…page won’t load.
I hope I’ve explained this well enough…and suggestions?
Thanks…great site btw.
Nevermind….I figured it out. I added e.stop(); to the “click” event function.
Not working in Opera.
@Ahmed Alfy: Use a different “trigger”, like double click.
Wow David, your work is impressive like always!!
i’ll try it ;D!
It’s not working perfectly in safari on OSX.
The menu will show, but you can’t use it as it should be. It’s ignoring the mouseover. You have to click on the menu first and hold down the mouseclick, move the cursor away from the menu, release the mouseclick and now you can use it.
Anyway.. nice menu! Keep up the good work!
Hey, just want to say I love this tool! I was actually wondering if it was somehow possible to pass the element to the onShow event, so I could change the menu based on the elements properties before the menu actually showed?
Thanks!
Acutally, I figured it out after reading over your code again. Thx :)
Hi David,
I’m not sure if anyone else noticed or was bothered by this, but when you right-click to get the context menu, in everything but opera apparently, there’s the potential that it will highlight text. You can prevent that by adding this to the show() function:
This is something I would like to see done in JQuery.
Hi There, i wonder if there is any way to put this context menu to every row in a table, of couse each item has to have a different menu so i can get for example a data from the row.
@Maximiliano: Yes, so set the “targets” to “#MyTable tr”
Hi Again, the thing your mention it works, so im trying right now to gather one element from the Table or something , can you write something that could help me?
I make it works with element.cells[1].innerText
There is anyway to know the element using the onShow Event?
Sorry, i figured out, i can use this
onShow: function(){
alert(this.options.element.cells[1].innerText);
}
great one David..!! thanks.. ^_^ but does it work on omnigrid ?
hello
wery nice script david. i want to use on whole body (wherever its right clicked) not on only one DIV … its there possible to make that?
thank you
Just thought I’d let you know that I’ve added an adapted version of this to my PDF annotation Moodle module, so thanks for making this code available!
(FYI the link to the Moodle module is above)
Hi,
sorry – looking at the code -but seems i’m stupid. Where you trigger right mouse click?
How to bring up contextmenu with a left-mouse click ??
Thanks
@Alfred: It’s the “trigger” option. You’d want:
trigger: ‘click’
ok, answering myself ;-)
trigger: ‘click’,
LOL – Thanks – i see you answered same :-)