Subscribe to Martyr2's Programming Underground        RSS Feed
-----

Context Menus With JQuery

Icon Leave Comment
I was tinkering around with some jQuery and created a little bit of code that I thought I would throw out into the Internet ether. I am sure it not the most efficiently optimized, but it does demonstrate some things can do with a little jQuery and the contextmenu event. I see this example as something you could easily build off of and with a little styling and a little more tinkering you could certainly create dozens of custom menus that could service a multitude of different projects. Menus that can have a state or possibly hold icons, linear gradients etc. At the end of this article I have provided a fiddle that demonstrates a little of what I am talking about here.

In our demonstration we will create a simple menu I called "boxMenu". This menu is an object literal that essentially contains a name, a target (the element that triggered the menu) and a list of menu options. Each menu option could contain text, a command function (for when it is clicked) and whether or not it is an active option or not. But before we reach that part I want to start off creating a simple div target which we will bind a context menu to...

Setup HTML and CSS

<div id="target"></div>



Here is some CSS to go long with our code that will add a bit of styling...

#target {
    border: #c0c0c0 solid 1px;
    background-color: #f2f2f2;
    width: 100px;
    height: 100px;
}

.menu {
    position: absolute;
    display: none;
    border: #c0c0c0 solid 1px;
    font-family: calibri, arial, helvetica, sans serif;
}

div .menuOption {
    padding: 4px 8px;
    background-color: #f0f0f0;
}

div .active:hover {
    cursor: pointer;
    background-color: #99cb33;
    color: #ffffff;
}

div .inactive {
    color: #c0c0c0;
}




Context Menu Binding in jQuery

So far so good. Now we will create a contextmenu event binding that will do a few things. It will make a call to a buildMenu function which will be responsible for building the menu (or returning an existing menu if it was already built) and then determine where to display it on the screen.


$("#target").bind("contextmenu",function(e){ 
    var newMenu = buildMenu(boxMenu,this);
		
    var winWidth = $(window).width();
    var winHeight = $(window).height();
		
    // Menu not off screen to right
    if ((e.pageX + newMenu.outerWidth()) > winWidth)
        newMenu.css("left", winWidth - newMenu.outerWidth());
    else 
        newMenu.css("left", e.pageX);
		
    // Menu not off screen at bottom
    if ((e.pageY + newMenu.outerHeight()) > winHeight) 
        newMenu.css("top", winHeight - newMenu.outerHeight());
    else
        newMenu.css("top", e.pageY);
		
    newMenu.show();
    return false; 
}); 




We will place the menu at the coordinates of the mouse unless the menu's borders would go outside the edge of the window object. In that case we would display it so that its entire menu region is visible. This is just like how context menus work today. We have one calculation for checking if the context menu is off the edge to the right and one if it is going off the edge towards the bottom.

Building the Context Menu

Below is the code we use to build the menu. It takes two pieces of information. The first is the menu variable holding the type of menu we want to build (our object literal). Think of this as a blueprint. You can add to it for all sorts of functionality and more tricks. The second argument is our target triggering the menu... passed in from the context menu bind function above. Here we are passing our "target" div we created earlier.

// Takes a menu variable and the target element, builds the HTML and returns a reference to the menu.
function buildMenu(menu, target) {
    if ($("#" + menu.name).length) {
        var m = $("#" + menu.name);
        m.hide();
        return m;
    }
    
    // Build overall menu
    var m = document.createElement("div");
    m.className = "menu";
    m.target = target;
    m.id = menu.name;
    
    // Build options for menu based on menu variable
    for (var i = 0; i < menu.items.length; i++) {
        var item = document.createElement("div");
        
        if (menu.items[i].active)
            item.className = "menuOption active";
        else
            item.className = "menuOption inactive";
        
        item.innerHTML = menu.items[i].text;
        item.onclick = menu.items[i].command;
        m.appendChild(item);
    }
    
    $("body").append(m);  
    return $(m);
}



If the menu had already been built before, we will simply select it with some jQuery selectors, hide it (in case it was showing from another element) and return it to be placed. Otherwise we will create it using the menu variable and append it to the body of the page. Our CSS will keep it hidden after being build and we will show it when we are ready. You may need to also adjust it's z-index if you have items on the page that may go over the top of the menu. We want our menus to have the highest z-index.

Context Menu "Map" Object Literal

So we are coming up on the part where we need to then create our menu object literal that will instruct the buildMenu function on how to build the menu. It is then used by the contextmenu event to place it on screen. Here we create a simple menu with two options. One that is active and another which is inactive. Active elements can be highlighted and clicked while the inactive are non clickable. As you will see, inactive menu items are stylized to be grayed out. When we click an item, the keyword "this" is going to be the actual div element that did the click. This means we have to check for the active/inactive class to determine its state before we can execute its command.

// Menus
var boxMenu = {
    name: "boxmenu",
    target: null,
    items: [{
        text: "Option 1",
        command: function() {
            // This is the menu option clicked
            if ($(this).hasClass("active")) {
                alert("Clicked option 1 and target is: " + target.id);
            }
        },
        active: true
    }, {
        text: "Option 2",
        command: doSomeFunction,
        active: false
    }]
};


// Example function of calling functions outside of a menu.
// Here "this" is going to refer to the option clicked.
function doSomeFunction() {
    if ($(this).hasClass("active")) {
        alert("Example of calling external function");
    }
}



Option 1 shows an example of calling the function inline while the second option would show how to call one externally. Again, not the cleanest but due to the fact that we are building actual HTML elements from the menu "map" it makes sense that we would want access to the actual element. This means that after we build the menu we have to then interact with the menu because it isn't being rebuilt each time.

Cleanup Code

Last little code here is for when we determine a click up of the left mouse key. It hides all menus. This does double duty for after we have clicked an option or when we changed our minds and clicked elsewhere on the page to hide the context menu.

// Clears all menus when click the document (as an example)
// Make your own custom trigger for when you want to dismiss them.
$(document).bind("mouseup", function(e) {
   if (e.which == 1) { $(".menu").hide(); }
});



So I believe that is all there is to the foundation of a possible context menu framework. Maybe clean up a few things and make it a little more optimized and you could be off to the races. Hope you guys enjoy it. I have included the fiddle below so you can play.

http://jsfiddle.net/5xkS4/

Don't forget to purchase our ebook which contains over 200 miscellaneous project ideas which contains projects for all levels and languages. Thanks for reading! :)

0 Comments On This Entry

 

August 2014

S M T W T F S
     12
3456789
10111213141516
17181920212223
242526 27 282930
31