joewalnes/jquery-simple-context-menu — Issue #6: added code to position pop-up menu
Author: rhysryan
Created: 2012-01-26 21:57:23+00:00
State: open

In my application I had various places where the button to active the pop-up menu was located. In addition I had various lengths of the menu as different menus had different number of items. And because I was using scrolling within a frame, I could not guarantee that the menu would not get cut off at the bottom. Therefore I wrote code to support controlling the top left position.

Here are the steps:

Replace the contextmenu.js file with the following code:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------

/**
* jQuery plugin for Pretty looking right click context menu.
*
* Requires popup.js and popup.css to be included in your page. And jQuery, obviously.
*
* Usage:
*
* $('.something').contextPopup({
* title: 'Some title',
* items: [
* {label:'My Item', icon:'/some/icon1.png', action:function() { alert('hi'); }},
* {label:'Item #2', icon:'/some/icon2.png', action:function() { alert('yo'); }},
* null, // divider
* {label:'Blahhhh', icon:'/some/icon3.png', action:function() { alert('bye'); }},
* ]
* });
*
* Icon needs to be 16x16. I recommend the Fugue icon set from: http://p.yusukekamiyamane.com/
*
* - Joe Walnes, 2011 http://joewalnes.com/
* https://github.com/joewalnes/jquery-simple-context-menu
*
* MIT License: https://github.com/joewalnes/jquery-simple-context-menu/blob/master/LICENSE.txt
*/
jQuery.fn.contextPopup = function(menuData) {

// Define default settings
var itemCount = 0;
var settings = {
contextMenuClass: 'contextMenuPlugin',
gutterLineClass: 'gutterLine',
headerClass: 'header',
seperatorClass: 'divider',
title: '',
items: []
};

// merge them
$.extend(settings, menuData);

// Build popup menu HTML
function createMenu(e) {

// build the menu class and add it to the html
var menu = $('

')
.appendTo(document.body);

// if there's a title, add it
if (settings.title) {
$('
').text(settings.title).appendTo(menu);
}

// build the menu items one at a time
settings.items.forEach(function(item) {

// add the menu item if it exists
if (item) {

// build the row and add it to the menu
var rowCode = '
';
var row = $(rowCode).appendTo(menu);

// add the icon
if (item.icon) {
var icon = $('');
icon.attr('src', item.icon);
icon.insertBefore(row.find('span'));
}

// add the menu text
row.find('span').text(item.label);
if (item.action) {
row.find('a').click(function(){ item.action(e); });
}

// add the separator
} else {
$('
').appendTo(menu);
}

});

// close out the menu
menu.find('.' + settings.headerClass ).text(settings.title);
return menu;

}

// On contextmenu event (right click)
// this.bind('contextmenu', function(e) {
this.bind('click', function(e) {

// get the offset to handle page length
var yCor = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
var popOff = 0;

if (settings.count) {
itemCount = settings.count * 10;
} else {
itemCount = 0;
}

// adjust the offset to make sure the entire pop-up menu can be seen
if (yCor < 250) {
popOff = -25;
}
else if (yCor > 250 && yCor < 500) {
popOff = (-1 * itemCount);
}
else if (yCor > 450) {
popOff = (-1 * itemCount) + (-1 * 100);
}

// Create and show menu
var menu = createMenu(e)
.show()
.css({zIndex:1000001, left:e.pageX - 110, top:e.pageY + popOff})
.bind('contextmenu', function() { return false; });

// Cover rest of page with invisible div that when clicked will cancel the popup.
var bg = $('

')
.css({left:0, top:0, width:'100%', height:'100%', position:'absolute', zIndex:1000000})
.appendTo(document.body)
.bind('contextmenu click', function() {
// If click or right click anywhere else on page: remove clean up.
bg.remove();
menu.remove();
return false;
});

// When clicking on a link in menu: clean up (in addition to handlers on link already)
menu.find('a').click(function() {
bg.remove();
menu.remove();
});

// Cancel event, so real browser popup doesn't appear.
return false;

});

// return back
return this;

};

Here's how it works:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1. as the menu is created, each item in the list counted
2. then that number is multiplied by 10 (you can adjust this number to suit your stilling)
3. then the code evaluates the y coordinate and calculates the offset for displaying it

What to adjust:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1. you can adjust the line multiplier (ie. 10) to handle your formatting
2. adjust the popOff calculations to get your offset to work for your needs