Tech-Stuff‎ > ‎

jQuery/asp.net: Dynamic Context Menu on Grid Column

posted May 29, 2009, 11:22 PM by Kartik Sehgal   [ updated May 31, 2009, 8:45 PM ]

In this Article we will create a context menu that appears on the click of cell in a grid column that may bring data specific to the menu.


Quick Walk through

Context Menu click through


Concept

We have Ajax Dropdown Menu extender available in AJAX controls from Microsoft. Though they are quick to integrate, they are tougher to customize and the page becomes slower if you are attaching them to grid rows where the number of rows is relatively high (50 or more). Faced with this problem, I decided to create a sample to create a dynamic, customizable context menu that opens on clicking the cell just like the menu extender without causing performance hindrance. 

In the sample, we have created a static grid as the focus was on context menu and not creating grids dynamically. It can work with gridview control also.

The cell content has been put inside a <button> tag which supports the onblur event which is key to closing the menu when the user clicks anywhere outside. For more about the <button> tag please feel free to go through Comparison between input type button and button tag

On the click of the button, the first time, a request is sent to an ASHX handler to get the menu from server (since we it is context sensitive). Subsequently, on clicks, it refers to the pre-loaded menu.

When the user clicks outside, the menu will disappear or when the user clicks on another link.

Sample Application

Please feel free to download the sample application: PopupMenuSample.zip

HTML Grid on which the Context Menu will trigger

<table cellspacing="0" cellpadding="2" border="1" style="table-layout: fixed; width: 600px; border: solid 1px #000; border-collapse: separate;"> <caption style="font-family: Verdana; font-size: 12pt; font-weight: bold;">Contact Listing</caption> <colgroup> <col width="200px" /> <col width="200px" /> <col width="200px" /> </colgroup> <thead> <tr> <th class="GridCellStyle">Contact Person</th><th class="GridCellStyle">Personal Email</th><th class="GridCellStyle">Personal Mobile</th> </tr> </thead> <tbody> <tr> <td class="GridCellStyle" ><button parent="GridTable" type="button" id="Button1" class="ItemOutStyle" >Kartik Sehgal <p> Unread emails (5) </p></button></td> <td class="GridCellStyle">kartiksehgal@gmail.com</td> <td class="GridCellStyle">91-011-00000000</td> </tr> <tr> <td class="GridCellStyle" ><button parent="GridTable" type="button" id="Button2" class="ItemOutStyle" >Pranav Kaushik <p> Unread emails (2) </p></button></td> <td class="GridCellStyle">pranavkaushik@gmail.com</td> <td class="GridCellStyle">91-011-00000000</td> </tr> <tr> <td class="GridCellStyle" ><button parent="GridTable" type="button" id="Button3" class="ItemOutStyle" >Mohd. Ilyas <p> Unread emails (1) </p></button></td> <td class="GridCellStyle">mohdilyas@gmail.com</td> <td class="GridCellStyle">91-011-00000000</td> </tr> <tr> <td class="GridCellStyle" ><button parent="GridTable" type="button" id="Button4" class="ItemOutStyle" >Bharat Sinha <p> Unread emails (12) </p></button></td> <td class="GridCellStyle">bharatsinha@gmail.com</td> <td class="GridCellStyle">91-011-00000000</td> </tr> <tr> <td class="GridCellStyle" ><button parent="GridTable" type="button" id="Button5" class="ItemOutStyle" >Sushil Kumar Maurya</button></td> <td class="GridCellStyle">sushilkumarmaurya@gmail.com</td> <td class="GridCellStyle">91-011-00000000</td> </tr> </tbody> </table>

The click event is attached to the button tags. One such tag has been highlighted above. The button tag was chosen because it supports onBlur event as well as multi-line text / images. It was considered a better choice than anchor tags (<a>) also because the anchor tag's mouse over would have worked only on the text area and we wanted it on the entire cell.

jQuery Plugin to attach Mouse Over / Out and Click events


jQuery.fn.setMouseEvents = function() { return this.each(function() { var me = jQuery(this); me.mouseover( function(e){me.attr("class", "ItemHoverStyle");}); me.mouseout( function(e){me.attr("class", "ItemOutStyle");}); me.click(function() { $("div").filter("[type=ContextMenu]").hide(); InstantiateMenu(me.attr("id")); }); me.blur(function() { closeMenu("Menu" + me.attr("id")); } ); }); };

Support jQuery Functions


function logMenuFocusOn(menuInstance) { $("#" + menuInstance).attr("mouseonmenu", "1"); } function logMenuFocusOut(menuInstance) { $("#" + menuInstance).attr("mouseonmenu", "0"); } function closeMenu(menuInstance) { if ($("#" + menuInstance).attr("mouseonmenu") == 0) { $("#" + menuInstance).hide(); } } function InstantiateMenu(ParentID) { if($("#Menu" + ParentID).html() == null) { var strMenuHTML = "<div type=\"ContextMenu\" id=\"Menu" + ParentID + "\" class=\"MenuStyle\" mouseonmenu=\"1\">loading...</div>"; $("body").append(strMenuHTML); $.post("MenuHandler.ashx?StrMethodName=GETMENU", {}, function(response) { $("#Menu" + ParentID).html(response); }); } var offset = $("#" + ParentID).offset(); $("#Menu" + ParentID).css({ "top": (offset.top + $("#" + ParentID).height() - 5) + "px" }).show(); $("#Menu" + ParentID).mouseout(function() { logMenuFocusOut("Menu" + ParentID); $("#" + ParentID)[0].focus(); } ); $("#Menu" + ParentID).mouseover(function() { logMenuFocusOn("Menu" + ParentID); } ); }

How to attach the Menu


$(document).ready( function() { $("button").filter("[parent=GridTable]").setMouseEvents(); } );

Here we have used the filter function in jQuery. Inside the filter function, we are searching for match where there is an attribute of type "parent" with value "GridTable". Overall, we will get all button tags where parent attribute exists and value is "GridTable". This is the beauty of jQuery. In one step, we have attached the mouse over  / out and click events on all the relevant tags.

Building the Menu


Inside the ASHX handler: private String CreateMenuHtml() { System.Text.StringBuilder strBldOptions = new System.Text.StringBuilder(); strBldOptions.Append("<table style=\"width:100%;\" cellspacing=\"0\"><tr><td class=\"MenuItem\"><a href=\"#\" onclick=\"alert('hi');\">View this item</a></td></tr><tr><td class=\"MenuItem\"><a href=\"#\">Edit this item</a></td></tr><tr><td class=\"MenuItem\"><a href=\"#\">Delete this item</a></td></tr></table>"); return strBldOptions.ToString(); } On the client side: function InstantiateMenu(ParentID) { if($("#Menu" + ParentID).html() == null) { var strMenuHTML = "<div type=\"ContextMenu\" id=\"Menu" + ParentID + "\" class=\"MenuStyle\" mouseonmenu=\"1\">loading...</div>"; $("body").append(strMenuHTML); $.post("MenuHandler.ashx?StrMethodName=GETMENU", {}, function(response) { $("#Menu" + ParentID).html(response); }); } var offset = $("#" + ParentID).offset(); $("#Menu" + ParentID).css({ "top": (offset.top + $("#" + ParentID).height() - 5) + "px" }).show(); $("#Menu" + ParentID).mouseout(function() { logMenuFocusOut("Menu" + ParentID); $("#" + ParentID)[0].focus(); } ); $("#Menu" + ParentID).mouseover(function() { logMenuFocusOn("Menu" + ParentID); } ); }

We are building the menu from the server side just to showcase that it can be done. If the requirement is to do it from client side, only the implementation inside InstantiateMenu() needs to be modified.

We fetch the menu from the server only if it has not already been created. Therefore, we are checking for null. Subsequently, we set the mouseout and mouseover events.

Browser Compatibility

This sample has been tested on IE6, IE7, IE8, Firefox3, Chrome2 and Safari4. It's functionality is compatible with all these browsers. The only difference would come in the UI representation which changes a bit from browser to browser. For example Safari puts a blue border on the outside of the selected item. Other than that, no known issue was found. Some level of customization may be required when being integrated.

Queries / Suggestions





ċ
PopupMenuSample.zip
(37k)
Kartik Sehgal,
May 31, 2009, 8:44 PM
Comments