HTML context menu without framework

Without a framework, add a context menu in JavaScript and CSS to the elements of a Web application or page.

This script is compatible with IE9, Firefox, Chrome, Safari.

It allows to replace the default menu appearing by clicking the right mouse button, by a menu specific to the object of the interface. If you just want to display a window or list when the mouse moves over an element, a tooltip will be used instead.

We know that we can detect a click of a button by the onClick attribute, but to meet the right button the onContextMenu attribute is used

The first step is to remove the default context menu. To do this simply return false.

<div oncontextmenu="return false"></div> 

As we will actually call a function in response to the event, the function must returns false and we returns the result of this function like this:

<div oncontextmenu="return myfunction()"></div> 

To build the contextual menu, the function generates a layer that can be filled dynamically with the list of menu items. For example, we add the command "Rename" and "Edit". The style sheet is included statically in the page that contains the script.

Here is a demonstration script, followed by the full source code.

Demonstration : Right-click

The HTML code is simple:

<div oncontextmenu="return mymenu(this)">
  Demonstration : Right-click
</div> 

The JavaScript code captures the current position of the mouse to place the generated menu above the corresponding element.

The function that we have defined for this purpose works on all modern browsers. As the script is for an application, you can ignore navigation tools of the past.

var xMousePosition = 0;
var yMousePosition = 0;
document.onmousemove = function(e)
{
  xMousePosition = e.clientX + window.pageXOffset;
  yMousePosition = e.clientY + window.pageYOffset;
};

Attributes pageXOffset and pageYOffset can take into account the scrolling of the page (which is useless when the interface does not scroll in the browser window).

It is still possible to ignore this function by placing the menu as a child of the corresponding element, rather than its container, and give it a fixed position next to the element.

The menu is then created and set according to the position of the mouse.

Full JavaScript code of the context menu

var xMousePosition = 0;
var yMousePosition = 0;
document.onmousemove = function(e) {
  xMousePosition = e.clientX + window.pageXOffset;
  yMousePosition = e.clientY + window.pageYOffset;
};

function rename(element) {
  alert("Rename");
}

function edit(element) {
  alert("Edit");
}

function mymenu(element) {
  var x = document.getElementById('ctxmenu1');
  if(x) x.parentNode.removeChild(x);

  var d = document.createElement('div');
  d.setAttribute('class', 'ctxmenu');
  d.setAttribute('id', 'ctxmenu1');
  element.parentNode.appendChild(d);
  d.style.left = xMousePosition + "px";
  d.style.top = yMousePosition + "px"; 
  d.onmouseover = function(e) { this.style.cursor = 'pointer'; } 
  d.onclick = function(e) { element.parentNode.removeChild(d);  }
  document.body.onclick = function(e) { element.parentNode.removeChild(d);  }

  var p = document.createElement('p');
  d.appendChild(p);
  p.onclick=function() { rename(element) };
  p.setAttribute('class', 'ctxline');
  p.innerHTML = "Rename";

  var p2 = document.createElement('p');
  d.appendChild(p2);
  p2.onclick=function() { edit(element) };  
  p2.setAttribute('class', 'ctxline');
  p2.innerHTML = "Edit"; 

  return false;
}

The functions rename() and edit() are for demonstration only and will be replaced by your own functions.

This code reflects two design choices:

  1. The menu is created dynamically by adding new tags to the DOM.
  2. There is no function to add items, a specific code is created for each command. It is enough if one has little context menus in the application, but must be developed if we have many. In fact it is quite easy to add a row using copy / paste and changing data.

The menu generated is equivalent to the following static code, which shows that it is more difficult to associate events with static tags:

<body onclick="document.getElementById('ctxmenu1').parentNode.removeChild(d);">
  <div class="ctxmenu" id="ctxmenu1"
       onmouseover="this.style.cursor = 'pointer'"
       onclick="this.parentNode.parentNode.removeChild(this)">
  <p class="ctxline" onclick="rename(element)">Rename</p>
  <p class="ctxline" onclick="edit(element)">Edit</p>
</div>

The variable element is the object which is associated with the context menu. In our example, it would be a filename in a list.

Full CSS code

.ctxmenu {
  position:absolute;	
  min-width: 128px;
  height:auto;
  padding: 8px;
  margin:0;
  margin-left:32px;
  margin-top:-16px;
  border: 1px solid #999;
  background: #F8F8F8;
  box-shadow: 2px 2px 2px #AAA;
  z-index:11;
  overflow: visible;
}
.ctxline {
  display:block;
  margin:0px;
  padding:2px 2px 2px 8px;
  border:1px solid #F8F8F8;
  border-radius:3px;
  font-size:13px;
  font-family:Arial, Helvetica, sans-serif;
  overflow:visible;
}
.ctxline:hover {
  border:1px solid #BBB;
  background-color: #F0F0F0;
  background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
  background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6);
  background-image: -webkit-gradient(linear, 0 0, 0 100%,
    from(#ffffff), to(#e6e6e6));
  background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
  background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
  background-image: linear-gradient(top, #ffffff, #e6e6e6);
  background-repeat: repeat-x;
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff',
    endColorstr='#e6e6e6', GradientType=0);
}

Some properties are essential:

The rest is a matter of look and feel.

We tried to make it look pretty close to that of a standard Windows environment. As the appearance of Windows elements actually depends on the theme chosen by the user, it is possible that the appearance is not that of context menus on your system. But it is difficult to do better in this regard ...

Limitation

When I use the context menu in production sometimes the browser assigns left and top properties incrementally adding to values that of the container.. This is in fact due to HTML 5 <!doctype html> and does not occur with the previous doctypes.
To work around this problem, You can add the menu as a child of the object to which it is associated and position the menu statically, with a negative value for top. But you can also change the doctype.

The best solution actually is to make the position of the container static (it is the default value):

#content
{
position:absolute;
left: 218px;
top: 92px;
}

To be replaced by :

#content
{
position:static;
margin-left: 218px;
margin-top: 92px;
}

That is what has been done to the current page. Then the top and left properties are correctly assigned to the menu.