近期由于项目中大量使用iframe导致父级页面的菜单显示后,在子页面的操作不能自动隐藏菜单。为此,笔者查看了extjs的源码,发现,extjs的菜单基本都是通过Ext.menu.MenuMgr进行管理的,我们在创建菜单时会自动调用其register方法,将菜单注册到管理器中,而管理器的初始化方法,针对document添加了mousedown事件,判断只要不是针对菜单选项的点击事件那么都会调用hideAll(遍历容器中的菜单对象并调用hide方法)方法。
以下为源码注释:如果有不正确的地方希望能不令赐教。
Ext.menu.Menu = Ext.extend(Ext.Container, {
. . .
initComponent : function() {
if (Ext.isArray(this.initialConfig)) {
Ext.apply(this, {
items : this.initialConfig
})
}
this.addEvents("click", "mouseover", "mouseout", "itemclick");
Ext.menu.MenuMgr.register(this); //注意这里将菜单本身放入到容器中。
if (this.floating) {
Ext.EventManager.onWindowResize(this.hide, this)
} else {
if (this.initialConfig.hidden !== false) {
this.hidden = false
}
this.internalDefaults = {
hideOnClick : false
}
}
Ext.menu.Menu.superclass.initComponent.call(this);
if (this.autoLayout) {
var a = this.doLayout.createDelegate(this, []);
this.on({
add : a,
remove : a
})
}
},
. . .
------------管理器代码,调用hideall可以隐藏所有菜单。
Ext.menu.MenuMgr = function() {
var g, d, c = {}, a = false, l = new Date();
function n() {//初始化方法
g = {};
d = new Ext.util.MixedCollection();
Ext.getDoc().addKeyListener(27, function() {
if (d.length > 0) {
i()
}
})
}
function i() {//隐藏方法
if (d && d.length > 0) {
var o = d.clone();
o.each(function(p) {
p.hide()
});
return true
}
return false
}
function e(o) {//隐藏事件处理
d.remove(o);
if (d.length < 1) {
Ext.getDoc().un("mousedown", m);
a = false
}
}
function k(o) {//显示事件处理
var p = d.last();
l = new Date();
d.add(o);
if (!a) {
Ext.getDoc().on("mousedown", m);
a = true
}
if (o.parentMenu) {
o.getEl().setZIndex(parseInt(o.parentMenu.getEl()
.getStyle("z-index"), 10)
+ 3);
o.parentMenu.activeChild = o
} else {
if (p && !p.isDestroyed && p.isVisible()) {
o.getEl().setZIndex(parseInt(p.getEl().getStyle("z-index"), 10)
+ 3)
}
}
}
function b(o) {//隐藏之前事件处理
if (o.activeChild) {
o.activeChild.hide()
}
if (o.autoHideTimer) {
clearTimeout(o.autoHideTimer);
delete o.autoHideTimer
}
}
function h(o) {//显示之前事件处理
var p = o.parentMenu;
if (!p && !o.allowOtherMenus) {
i()
} else {
if (p && p.activeChild) {
p.activeChild.hide()
}
}
}
function m(o) {//绑定document的鼠标事件
if (l.getElapsed() > 50 && d.length > 0 && !o.getTarget(".x-menu")) {
i()
}
}
return {
hideAll : function() {
return i()
},
register : function(o) {
if (!g) {
n()
}
g[o.id] = o;
o.on({
beforehide : b,
hide : e,
beforeshow : h,
show : k
})
},
get : function(o) {
if (typeof o == "string") {
if (!g) {
return null
}
return g[o]
} else {
if (o.events) {
return o
} else {
if (typeof o.length == "number") {
return new Ext.menu.Menu({
items : o
})
} else {
return Ext.create(o, "menu")
}
}
}
},
unregister : function(o) {
delete g[o.id];
o.un("beforehide", b);
o.un("hide", e);
o.un("beforeshow", h);
o.un("show", k)
},
registerCheckable : function(o) {
var p = o.group;
if (p) {
if (!c[p]) {
c[p] = []
}
c[p].push(o)
}
},
unregisterCheckable : function(o) {
var p = o.group;
if (p) {
c[p].remove(o)
}
},
onCheckChange : function(q, s) {
if (q.group && s) {
var u = c[q.group], p = 0, o = u.length, t;
for (; p < o; p++) {
t = u[p];
if (t != q) {
t.setChecked(false)
}
}
}
},
getCheckedItem : function(q) {
var s = c[q];
if (s) {
for (var p = 0, o = s.length; p < o; p++) {
if (s[p].checked) {
return s[p]
}
}
}
return null
},
setCheckedItem : function(q, t) {
var s = c[q];
if (s) {
for (var p = 0, o = s.length; p < o; p++) {
if (s[p].id == t) {
s[p].setChecked(true)
}
}
}
return null
}
}
}();
//这里需要注意的是我们在调用hideAll方法前,需要想源码中一样确认下,事件的触发对象不是menu中的元素,否则的话,容易使一些带有菜单的组件如(时间控件)的功能失效。