This script will use my mouse position capture script to draw the menus at the proper coordinates.
The object constructor will accept three parameters:
- id - HTML id of the menu)
- menuItems - an array containing all the menu items which will be drawn
- parent - optional parameter which defines the element to which the menu will be applied. If this is not set, document.body will be used.
Here is the object constructor:
function contextMenu(id, menuItems, parent) { //First we need to make sure that we have the obligatory parameters present if(typeof id == 'undefined' || typeof menuItems == 'undefined') return; //Remember menu's id cause we will need it in the object's prototypes this.id = id; //If this menu already exists - call a prototype that destroys it, so it can be created again on it's new place. //This is executed when we already have the menu drawn on the page and the user has clicked on a new location if(document.getElementById(this.id)) this.destroy(); //Create the main menu item - an UL element. I use some styles to make it look pretty much like every browsers' menu, //which will be more familiar to the user var menu = document.createElement('ul'); menu.id = id; menu.style.listStyleType = 'none'; menu.style.margin = 0; menu.style.padding = 0; menu.style.position = 'absolute'; menu.style.left = xMousePos; menu.style.top = yMousePos; menu.style.border = '1px solid #000;'; menu.style.background = '#eee'; menu.onmouseover = function() { this.style.cursor = 'pointer'; } menu.oncontextmenu = function() { return false; } //We have the menu created, now we must append it's items. for(k in menuItems) { //Sometimes we need to divide the menus in sections. To do so, I'm using a keyword - 'separator'. //If this keyword is used in any menu item, it will be drawn as a separating line (hr) between //the elements above and beneath it... if(menuItems[k] == "separator") { var delim = document.createElement('hr'); delim.style.height = '1px'; delim.style.width = '90%'; delim.style.margin = 0; delim.style.marginLeft = '5%'; delim.style.padding = 0; menu.appendChild(delim); } else //...otherwise we use the text and code from the menuItems array { //if this is not a valid function - don't create the menu item if(typeof menuItems[k] != "function") continue; var menuItem = document.createElement('li'); menuItem.style.textAlign = 'left'; menuItem.style.margin = 0; menuItem.style.marginLeft = '5px'; menuItem.style.marginRight = '5px'; menuItem.style.padding = 0; menuItem.innerHTML = k; menuItem.onclick = menuItems[k]; menu.appendChild(menuItem); } } //Now we have the menu ready we need to append it to it's parent element. //If the third parameter (parent) is omitted - we use the body of the page. //Note that the check 'if(typeof parent == "object")' will also fail if we have provided a parent, //but it is not a valid HTML element, thus the document.body will be used instead. if(typeof parent == "object") parent.appendChild(menu); else document.body.appendChild(menu); //The last thing we need to do is add an event listener to the parent. //We add an onclick event, so when the user clicks outside the menu it will disappear. //WARNING! This code overwrites the parent's onclick event listener (if any)! This means //that if you already have a listener defined, it will stop working! Be careful when using //this menu on elements the already have event listeners and always test carefully when implementing! menu.parentNode.onclick = methodize(this.destroy, this); }This is just a simple menu and the object has just one prototype, which removes the menu from the page:
//The prototype that removes the menu from the page. contextMenu.prototype.destroy = function() { document.getElementById(this.id).parentNode.removeChild(document.getElementById(this.id)); }I will extend this object in the future, allowing customization, submenus and other useful functionality. To use the code we need to append an event listener to the oncontextmenu event to each element where the menu will be displayed. We can create a custom function that defines our menu items and creates the menu:
//This is an example function that creates a custom right-click menu function createContextMenu(id, parent) { var menuItems = new Array(); //You can define a new function using a "function() {...}" code as the value of the array. menuItems["Go Back"] = function() { history.go(-1) }; //You can add a separator by defining a value "separator" for the array element. menuItems[0] = "separator"; menuItems["Reload"] = function() { location.reload(); }; //You can also use a predefined function - in this case reload(), which is defined in the code that fallows. menuItems["My Own Reload"] = reload; menuItems[1] = "separator"; menuItems["Another Function"] = anotherFunction; new contextMenu(id, menuItems, parent); return false; } //An example custom function that is called when a menu item is clicked function reload() { location.reload(); } //An example custom function that is called when a menu item is clicked function anotherFunction() { alert('Another Function Code...'); }This function defines an array, which will be used to draw the menu. Some of the functions inside the menu are created with the code "function() {...}" and others are predefined functions, which are also shown in the code above. Note that the keyword 'separator' is a string, so you can still have a function named separator() and use it within the menu. In order for the menu to be working propery, all values of the array must be valid functions. If you misspell a function name, or set a value that is not a function - the menu item will not be displayed.
Finally, a sample HTML that shows you how to use the object:
<div style='width: 250px; height: 250px; border: 5px solid #333;' oncontextmenu="createContextMenu('myFirstContextMenu', this); return false;"> </div>With this code, a menu will appear at the mouse position when you right click on the div. If you click anywhere outside that div, the page will be working normally. WARNING! When you create a menu with this code it overwrites the parent's onclick event listner (if any)! This means that if you already have a listener defined, it will stop working, which can lead to strange page behavior or brake other scripts' usage! Be careful when using this menu on elements the already have event listeners and always test carefully when implementing!
This is pretty much the simplest menu example. In the future fallow ups to this post I will extend the object to be able to create a menu with sub menus, add the ability to set a custom style for the menu, etc. You can see a demo page here. You can use the script directly from this url.
P.S. I've received a bug report about this script - it isn't working on Internet Explorer! Unfortunately I had no Windows OS around me when I was creating it and haven't tested on IE. I will fix it soon and advice you to wait for the fix before using the script.