Menu.prototype.AttachEvents
=
function
(menuHtml)
{
var menuObj = __MenuCache__[menuHtml.uniqueId];
if ( menuObj.m_IsEventAttached )
{
return ;
}
var doc = menuObj.m_Popup.document;
for ( var i = 0 ; i < menuObj.m_Items.length ; ++ i )
{
var mItem = menuObj.m_Items[i];
var trItem = doc.getElementById(mItem.m_Id);
if ( mItem.m_Action && ! mItem.m_ChildMenu )
{
if ( ! mItem.IsSeparator() )
{
trItem.attachEvent('onclick', mItem.m_Action);
trItem.attachEvent('onclick', Menu.HideAllPopups);
}
}
if ( mItem.m_ChildMenu )
{
trItem.attachEvent('onmousedown', this .InnerShow);
}
if ( ! mItem.IsSeparator() )
{
trItem.attachEvent('onmouseover', this .ActiveItem);
}
}
menuHtml.attachEvent('onmouseout', this .ResumeItem);
if ( doc )
{
doc.attachEvent('onkeydown', this .Keydown);
}
menuObj.m_IsEventAttached = true ;
};
{
var menuObj = __MenuCache__[menuHtml.uniqueId];
if ( menuObj.m_IsEventAttached )
{
return ;
}
var doc = menuObj.m_Popup.document;
for ( var i = 0 ; i < menuObj.m_Items.length ; ++ i )
{
var mItem = menuObj.m_Items[i];
var trItem = doc.getElementById(mItem.m_Id);
if ( mItem.m_Action && ! mItem.m_ChildMenu )
{
if ( ! mItem.IsSeparator() )
{
trItem.attachEvent('onclick', mItem.m_Action);
trItem.attachEvent('onclick', Menu.HideAllPopups);
}
}
if ( mItem.m_ChildMenu )
{
trItem.attachEvent('onmousedown', this .InnerShow);
}
if ( ! mItem.IsSeparator() )
{
trItem.attachEvent('onmouseover', this .ActiveItem);
}
}
menuHtml.attachEvent('onmouseout', this .ResumeItem);
if ( doc )
{
doc.attachEvent('onkeydown', this .Keydown);
}
menuObj.m_IsEventAttached = true ;
};
由于attachEvent方法的操作类似一个压栈的过程,执行它是不会检查同一个事件是否已被attach了的,所以需要自己记录菜单是否已attach事件的标志,在执行attach操作前判断。第二个要注意的是,在向事件上attach处理方法时,由于方法都是Menu类内部的方法,都是使用this.MethodName来引用的,而在这些方法的实现中,是不能使用this这个关键字来指代Menu类的当前实例的,我下面用this.ResumeItem的调用来说明,ResumeItem()的源代码如下:
Menu.prototype.ResumeItem
=
function
(evt)
{
if ( ! evt || evt.toElement )
{
return ;
}
var menuHtml = FindParentElement(evt.srcElement, 'TABLE');
if ( ! menuHtml.uniqueId )
{
menuHtml = FindParentElement(menuHtml.parentElement, 'TABLE');
}
var menuObj = __MenuCache__[menuHtml.uniqueId];
if ( menuObj.HasSubMenuExpanded() )
{
return ;
}
if ( menuObj.m_ShowTimer )
{
window.clearTimeout(menuObj.m_ShowTimer);
menuObj.m_ShowTimer = null ;
}
menuObj.__resumeItem();
};
{
if ( ! evt || evt.toElement )
{
return ;
}
var menuHtml = FindParentElement(evt.srcElement, 'TABLE');
if ( ! menuHtml.uniqueId )
{
menuHtml = FindParentElement(menuHtml.parentElement, 'TABLE');
}
var menuObj = __MenuCache__[menuHtml.uniqueId];
if ( menuObj.HasSubMenuExpanded() )
{
return ;
}
if ( menuObj.m_ShowTimer )
{
window.clearTimeout(menuObj.m_ShowTimer);
menuObj.m_ShowTimer = null ;
}
menuObj.__resumeItem();
};
继续说this引用的问题,当这个onmouseout事件被触发时,方法默认的第一个参数是其window上的event,这个我在使用...(3)中说过,还用红色标识出来了的。而这时ResumeItem里的this引用的是当前菜单所在的父IE窗口,所以获得菜单实例的方法仍然是从__MenuCache__里去取(代码中加下划线的语句)。同时除了ResumeItem,所有在AttachEvents里的attach的方法,InnerShow、ActiveItem和Keydown等,里面都不会有对this的调用(除非确实要引用父IE窗口)。
to be continued ...