Javascript多级菜单(一)

一、开篇

一直都苦于找不到合适的菜单,最近自己做了一个,感觉收获不小,拿出来分享。先看效果:

二、原理

  • 1、关于鼠标事件

    首先说一下mouseover和mouseout这两个事件,在IE和其他浏览器有一些差别。
    在IE中,当发生mouseover事件的时候,e.srcElement可以获得鼠标移入的元素,e.fromElement可以获得鼠标是从哪个元素移入的,e.toElement就是e.srcElement;
    在IE中,当发生mouseout事件的时候,e.srcElement可以获得鼠标移出的元素,e.fromElement和e.srcElement是一样的,e.toElement可以获得鼠标移动到当前的元素;
    在DOM中,mouseover和mouseout所发生的元素可以通过e.target来访问,相关元素是通过e.relatedTarget来访问的(在mouseover中相当于IE的e.fromElement,在mouseout中相当于IE的e.toElement);

    对于这样一种标签的嵌套关系,对ul注册mouseover和mouseout事件,鼠标从ul外面移入到最里面的a上面,会出现什么情况?
    答案是会出现三次mouseover和两次mouseout,虽然没有直接给li和a注册事件,但是由于事件的冒泡,也会被ul注册的mouseover和mouseout事件响应。这样一来,整过过程就是这样的:
    在IE中

    order type srcElement fromElement toElement
    1 mouseover ul 其他 ul
    2 mouseout ul ul li
    3 mouseover li ul li
    4 mouseout li li a
    5 mouseover a li a

    在DOM中
    order type target relatedTarget
    1 mouseover ul 其他
    2 mouseout ul li
    3 mouseover li ul
    4 mouseout li a
    5 mouseover a li

    如果我想在移入ul的时候,只响应一次mouseover和mouseout(无论移动到ul里面的li还是里面的a上面),应该如何做呢?

$("childItems").onmouseover = function(e){
    e 
= e||window.event;
    
var target = e.target||e.srcElement;
    
var relatedTarget = e.relatedTarget || e.fromElement;
    
if(!$(relatedTarget).descendantOf(this&& $(relatedTarget) != this){
        clearTimeout(timeoutId);
        timeoutId 
= null;
    }
}
$(
"childItems").onmouseout = function(e){
    e 
= e||window.event;
    
var target = e.target||e.srcElement;
    
var relatedTarget = e.relatedTarget ||e.toElement;
    
if(!$(relatedTarget).descendantOf(this&& $(relatedTarget) != this){//如果relatedTarget不是ul本身或者不是ul的子元素
        close();
    }    
}

mouseover事件发生的时候,判断relatedTarget(IE中的fromElement),如果relatedTarget是其他元素(不是ul的子元素也不是ul本身,表示鼠标从外面移入ul)才会响应,而在ul移入li或者li移入a的时候就不会相应。
同理,发生mouseout的时候,判断relatedTarget(IE中的toElement),如果relatedTarget是其他元素(不是ul的子元素也不是ul本身,表示鼠标从ul或者其子元素移出)才会响应,而在a移出到li或者li移出到ul的时候就不会相应。

  • 2、简单菜单

    鼠标移动到button上,则弹出子菜单;
    鼠标移开button,则开始关闭菜单的setTimeout;
    在timeout还未到的时候移入子菜单,则clearTimeout;
    鼠标移出子菜单,则开始关闭菜单的setTimeout;
    此时鼠标如果再移动到button上或者是移回到子菜单上,都需要clearTimeout,所以的mouseover也要先clearTimeout;

     

  • 3、多级菜单的逻辑


    在途中,对于item1-3这个MenuItem,depth属性(深度)为1(根菜单的深度为0,其子菜单深度为1,以此类推);childMenu是子菜单,当鼠标移动到MenuItem上面的时候,其子菜单则会显示;items属性是子菜单的MenuItem集合;parent属性是对上一级的MenuItem的引用。
    多级菜单比起简单菜单,各个MenuItem之间的逻辑关系是非常重要的:
    总的来说,鼠标移出任何一个菜单,都要开始计时关闭所有的菜单
    当鼠标移入任何一个菜单,除了显示他的子菜单(如果有的话)之外,还需要做两件事情:
    1、 显示他的所有父菜单,如果父菜单已经显示的话,则清除计时关闭。这是通过一下代码来实现的

    open:function(){
        
    this.clearCloseTimeout();
        
    if(this.childMenu)
            
    this.childMenu.show();
    },
    var temp = self;
    while(temp){
        temp.open();
        temp 
    = temp.parent;
    }


    2、 关闭所有兄弟菜单的子菜单,因为当前菜单可能会弹出自己的子菜单,所以这个时候要关闭所有的兄弟菜单的子菜单。这是通过以下代码来实现的:

    var items = self.parent?self.parent.items:self.menu.rootItems;
    items.each(
    function(item){
        
    if(self != item)
            item.closeAll();
    });


三、代码

Menu类
MenuItem类
使用方法

四、下载

        点此下载示例

posted @ 2008-11-07 00:27 LongWay 阅读(...) 评论(...) 编辑 收藏