为了比较直观的说明代码的作用,我就从菜单的显示开始说。要显示一个菜单显必须构建一个菜单的实例,实例构建的完整代码如下:
<div oncontextmenu="return ShowContextMenu(this);" style="width: 200; height: 200; border: solid 1px blue;">
<table width="100%" height="100%" border="0">
<tr>
<td valign="middle" align="center">
right click me
</td>
</tr>
</table>
</div>
<table width="100%" height="100%" border="0">
<tr>
<td valign="middle" align="center">
right click me
</td>
</tr>
</table>
</div>
<script language="javascript">
function ShowContextMenu(elmt)
{
if ( !elmt.contextMenu )
{
elmt.contextMenu = CreateContextMenu();
}
var win = window;
elmt.contextMenu.Show(win);
return false;
}
CreateMenu#region CreateMenu
function CreateContextMenu()
{
var menu = new Menu();
for ( var i=1 ; i < 6 ; i++ )
{
var mi = new MenuItem('Menu Item 1-' + i, Alert);
menu.Add(mi);
}
var submenu = new Menu();
for ( var i=1 ; i < 5 ; i++ )
{
var mi = new MenuItem('Menu I&tem 3-' + i, Alert);
submenu.Add(mi);
}
var submenu2 = new Menu();
submenu2.Add(new MenuItem('&Menu Item 2-1', null, null, null, submenu));
var mi2 = new MenuItem('Me&nu Item 1-6', Alert, 'images/paste.small.png', null, submenu2);
menu.AddAt(mi2, 2);
return menu;
}
#endregion
</script>
function ShowContextMenu(elmt)
{
if ( !elmt.contextMenu )
{
elmt.contextMenu = CreateContextMenu();
}
var win = window;
elmt.contextMenu.Show(win);
return false;
}
CreateMenu#region CreateMenu
function CreateContextMenu()
{
var menu = new Menu();
for ( var i=1 ; i < 6 ; i++ )
{
var mi = new MenuItem('Menu Item 1-' + i, Alert);
menu.Add(mi);
}
var submenu = new Menu();
for ( var i=1 ; i < 5 ; i++ )
{
var mi = new MenuItem('Menu I&tem 3-' + i, Alert);
submenu.Add(mi);
}
var submenu2 = new Menu();
submenu2.Add(new MenuItem('&Menu Item 2-1', null, null, null, submenu));
var mi2 = new MenuItem('Me&nu Item 1-6', Alert, 'images/paste.small.png', null, submenu2);
menu.AddAt(mi2, 2);
return menu;
}
#endregion
</script>
生成的Context Menu效果 |
这是完全手工添加菜单条目生成的一个Context Menu,最后会完成一个自动解析菜单数据来生成菜单的方法。
我们现在来看这个Menu.prototype.Show(win)方法,这是菜单的第一级显示时的方法,它是由用户来调用的,因为菜单需要定位于用户给定位置(ContextMenu的用户给定位置就是鼠标点击的位置)。在菜单显示出第一级后,后续的子菜单的显示,都是在Menu类内部来处理的,子菜单位置是相对于parent menu,后续逻辑就都封装在Menu类内部了。Show()方法代码如下:
Menu.prototype.Show = function(win)
{
if ( !win )
{
return;
}
var menuObj = this;
menuObj.m_Opener = win;
menuObj.__resumeItem();
var win = menuObj.m_Opener;
var popup, popwin, popdoc;
// 判断菜单的容器popup是否建立
if ( !menuObj.m_Popup )
{
popup = win.createPopup();
popup.document.body.bgColor = 'windowtext';
popup.document.body.style.backgroundColor = 'window';
menuObj.m_Popup = popup;
}
else
{
popup = menuObj.m_Popup;
menuObj.__resumeAll();
}
popdoc = popup.document;
popwin = popdoc.parentWindow;
// 判断是否需要重绘菜单的内容
if ( menuObj.m_Invalidate || !menuObj.m_Drawn )
{
popdoc.body.innerHTML = menuObj.Render().outerHTML;
// popdoc.body.appendChild(menuObj.Render());
menuObj.m_Invalidate = false;
menuObj.m_Drawn = true;
}
// 获取菜单的主table(菜单是使用table来实现的)
var menuHtml = popup.document.getElementById('menu');
// 这个show只是为了测量菜单的bounds而调用的
popup.show(0, 0, 1, 1);
var w = popdoc.body.scrollWidth;
// 判断菜单条目的Text的显示宽度是否在许可范围内,
// 如果超出许可范围则ellipsis处理并返回新的MenuItem的width
w = this.__isEllipsis(this, menuHtml);
var h = popdoc.body.scrollHeight;
var x = win.event.clientX + win.screenLeft;
var y = win.event.clientY + win.screenTop;
popup.show(x, y, w, h);
// 菜单的显示特效,使用filter实现的
this.FadeinEffect(Menu.Attributes.ShowMenuEffect);
menuObj.m_Bounds =
{
top: x, left: y,
width: menuHtml.offsetWidth,
height: menuHtml.offsetHeight
};
// 把菜单操作的事件attach到菜单上,鼠标和键盘操作等
menuObj.AttachEvents(menuHtml);
};
{
if ( !win )
{
return;
}
var menuObj = this;
menuObj.m_Opener = win;
menuObj.__resumeItem();
var win = menuObj.m_Opener;
var popup, popwin, popdoc;
// 判断菜单的容器popup是否建立
if ( !menuObj.m_Popup )
{
popup = win.createPopup();
popup.document.body.bgColor = 'windowtext';
popup.document.body.style.backgroundColor = 'window';
menuObj.m_Popup = popup;
}
else
{
popup = menuObj.m_Popup;
menuObj.__resumeAll();
}
popdoc = popup.document;
popwin = popdoc.parentWindow;
// 判断是否需要重绘菜单的内容
if ( menuObj.m_Invalidate || !menuObj.m_Drawn )
{
popdoc.body.innerHTML = menuObj.Render().outerHTML;
// popdoc.body.appendChild(menuObj.Render());
menuObj.m_Invalidate = false;
menuObj.m_Drawn = true;
}
// 获取菜单的主table(菜单是使用table来实现的)
var menuHtml = popup.document.getElementById('menu');
// 这个show只是为了测量菜单的bounds而调用的
popup.show(0, 0, 1, 1);
var w = popdoc.body.scrollWidth;
// 判断菜单条目的Text的显示宽度是否在许可范围内,
// 如果超出许可范围则ellipsis处理并返回新的MenuItem的width
w = this.__isEllipsis(this, menuHtml);
var h = popdoc.body.scrollHeight;
var x = win.event.clientX + win.screenLeft;
var y = win.event.clientY + win.screenTop;
popup.show(x, y, w, h);
// 菜单的显示特效,使用filter实现的
this.FadeinEffect(Menu.Attributes.ShowMenuEffect);
menuObj.m_Bounds =
{
top: x, left: y,
width: menuHtml.offsetWidth,
height: menuHtml.offsetHeight
};
// 把菜单操作的事件attach到菜单上,鼠标和键盘操作等
menuObj.AttachEvents(menuHtml);
};
to be continued ...