Introduction This treat is for those that really hate how the ASP.NET Menu works. The menu shows the sub-menu when the user hovers the mouse over the root level menu items. This can get annoying especially if you accidentally pass the mouse over the menu when trying to click something inside the page. This can be really frustrating from a usability point of view especially when developing Web Applications (not just websites). So, the following javascript will override the default behavior of the menu and show the second level of menu items only when the user clicks on the first (root) level menu items. Cleint side script function addClickBehavior(menuTable)
{
var tbody = menuTable.getElementsByTagName("TBODY")[0];
var tr = tbody.getElementsByTagName("TR")[0];
for(var i = 0; i < tr.childNodes.length; i++)
{
var td = tr.childNodes[i];
if(td.tagName && td.tagName.toLowerCase() == 'td')
{
var anchor = td.getElementsByTagName("A")[0];
if(anchor)
{
var onClick = td.onmouseover;
td.onclick =
(function (el, method){
return function(evt){
method.call(el);
if(window.event) {
evt = window.event
}
evt.cancelBubble = true;
};
})(td, onClick);
td.onmouseover =
(function (el){
return function(){
Menu_HoverRoot(el);
};
})(td);
//add cursor style
anchor.style.cursor = "default";
anchor.onclick = function(){return false;};
//td.onmouseout = null;
}
}
}
}
function WebForm_RemoveClassName(element, className) {
var current = element.className;
var oldLength = -1;
if (current) {
while(oldLength != current.length)
{
if (current.substring
(current.length - className.length - 1,
current.length) == ' ' + className) {
element.className =
current.substring
(0, current.length - className.length - 1);
oldLength = current.length;
current = element.className;
continue;
}
if (current == className) {
element.className = "";
oldLength = current.length;
current = element.className;
continue;
}
var index = current.indexOf(' ' + className + ' ');
if (index != -1) {
element.className =
current.substring
(0, index) +
current.substring
(index + className.length + 2, current.length);
oldLength = current.length;
current = element.className;
continue;
}
if (current.substring
(0, className.length) == className + ' ') {
element.className =
current.substring
(className.length + 1, current.length);
}
current = element.className;
oldLength = current.length;
}
}
}
function Menu_HoverRoot(item) {
var node = (item.tagName.toLowerCase() == "td") ?
item:
item.cells[0];
var data = Menu_GetData(item);
if (!data) {
return null;
}
var nodeTable = WebForm_GetElementByTagName(node, "table");
if (data.staticHoverClass) {
//avoids adding the same class twice
nodeTable.hoverClass = data.staticHoverClass;
WebForm_AppendToClassName(nodeTable, data.staticHoverClass);
}
node = nodeTable.rows[0].cells[0].childNodes[0];
if (data.staticHoverHyperLinkClass) {
node.hoverHyperLinkClass = data.staticHoverHyperLinkClass;
WebForm_AppendToClassName
(node, data.staticHoverHyperLinkClass);
}
return node;
} Also to call the function, add the following in the overriden OnPrerender method: Page.ClientScript.RegisterStartupScript
(Page.GetType(), "addClickBehavior",
"addClickBehavior(document.getElementById('" +
Menu1.ClientID + "'));", true);
The above code takes care of more than just adding click behavior to the menu. It also makes sure that styling remains intact (for example when the mouse is above the menu item it changes color, depending on how you style the menu).
Now as for where to place this javascript, I recommend a common place where it will be used across all pages, also adding them to a js file and referencing it would be fine. For example, if you have a User Control called "Top_Section.ascx", and that control has the menu inside it, then add the javascript to the top of that file before the menu.
I am not going to go into the details of the code since this is basically code taken from Microsoft's javascript for the Menu control and modified to our needs. The reason why this would work (since you will not call these functions explicitly), is that the original versions of these functions are called by the Menu Control itself, and since we wrote our own implementation of those functions (having the same name as the original ones we want to replace) the new implementation will override the default one as long as they appear after. That is how overriding is simulated in javascript. And these functions will definitely be parsed after the default ones because ASP.NET loads its resources first and before any user defined functions do. You can check that by viewing the source of the page. Also to know what I changed you can compre these functions to their originals by requesting the resources generated by ASP.NET (the ones that coontain "ScriptResource.axd"). Hope this helps you out. Thank you Muhammad my blog
|