![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLoRXZlRXbl9CXz52bjlGdv1WRvwVbvNmLzd2bsJmbj5yd3d3Lc9CX6MHc0RHaiojIsJye.gif)
。
為了比較直覺的說明代碼的作用,我就從菜單的顯示開始說。要顯示一個菜單顯必須建構一個菜單的執行個體,執行個體建構的完整代碼如下:
<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>
<script language="javascript">
function ShowContextMenu(elmt)
{
if ( !elmt.contextMenu )
{
elmt.contextMenu = CreateContextMenu();
}
var win = window;
elmt.contextMenu.Show(win);
return false;
}
CreateMenu
生成的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);
};
上面注解應該都比較清楚了,隻是這個popup.show(0, 0, 1,
1);比較有意思哈,當我們向popup裡添加好了菜單的HTML元素後,我們發現在popup沒有顯示過之前,是根本取不到構成Menu
UI的那個Table
element的bounds資訊的。這裡show上一下後,就是為了讓IE算出其bounds資訊,然後再使用實際的bounds資訊show菜單。這算一個小hack吧,也是這個Menu中比較有效率的地方,因為除了這個show(0,
0, 1,1)就在沒有計算菜單bounds的地方了,當然也用不着了。然而為什麼又沒有把bounds計算也做成lazy
load象popup的生成那樣呢?是因為使用者可能在菜單顯示後修改IE的字型大小(比如按住Ctrl再滾動滑鼠滾輪),這樣保證了再次顯示菜單時能修正菜單的實際bounds。而後面把menu的bounds存了起來是為了在顯示子菜單時,友善判斷其預設向右展開空間是否足夠,如果不夠寬則從parent
menu左側展開。
本文轉自部落格園鳥食軒的部落格,原文連結:http://www.cnblogs.com/birdshome/,如需轉載請自行聯系原部落客。