|
源代码网推荐
本文示例源代码或素材下载
一、开篇
一直都苦于找不到合适的菜单,最近自己做了一个,感觉收获不小,拿出来分享。先看效果:





二、原理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; vartarget=e.target||e.srcElement; varrelatedTarget=e.relatedTarget||e.fromElement; if(!$(relatedTarget).descendantOf(this)&&$(relatedTarget)!=this){ clearTimeout(timeoutId); timeoutId=null; } } $("childItems").onmouseout=function(e){ e=e||window.event; vartarget=e.target||e.srcElement; varrelatedTarget=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(); }, vartemp=self; while(temp){ temp.open(); temp=temp.parent; }
2、 关闭所有兄弟菜单的子菜单,因为当前菜单可能会弹出自己的子菜单,所以这个时候要关闭所有的兄弟菜单的子菜单。这是通过以下代码来实现的:
varitems=self.parent?self.parent.items:self.menu.rootItems; items.each(function(item){ if(self!=item) item.closeAll(); });
三、代码
Menu类
varMenu=Class.create({ initialize:function(childMenuClassName){ this.rootItems=[]; this.currentItem=null;//当前展开的MenuItem this.childMenuClassName=childMenuClassName; }, addItem:function(rootItem){ rootItem.depth=0; rootItem.parent=null; rootItem.menu=this; this.rootItems.push(rootItem); }, render:function(){ this.rootItems.each(function(item,index){ item.render(); }); } });
MenuItem类
varMenuItem=Class.create({ initialize:function(element){ this.element=$(element); this.items=null; this.closeTimeoutId=null; this.menu=null; this.childMenu=null; this.depth=0; this.parent=null; }, addItem:function(menuItem){ if(!this.items) this.items=[]; menuItem.parent=this; menuItem.depth=this.depth+1; menuItem.menu=this.menu; this.items.push(menuItem); }, isParentOf:function(childItem){//判断当前item是不是childItem的parent vartemp=childItem; while(temp.parent){ if(temp.parent==this) returntrue; temp=temp.parent; } returnfalse; }, topItem:function(){ vartemp=this; while(temp){ if(temp.depth==0) returntemp; temp=temp.parent; } returntemp; }, render:function(){ varself=this; functionelementMouseOver(e){ //关闭所有兄弟菜单 varitems=self.parent?self.parent.items:self.menu.rootItems; items.each(function(item){ if(self!=item) item.closeAll(); }); self.clearCloseTimeout(); if(self.depth==0){ self.childMenu.setStyle({ "top":self.element.cumulativeOffset().top+self.element.getHeight()+"px", "left":self.element.cumulativeOffset().left+"px" }); }else{ self.childMenu.setStyle({ "top":self.element.cumulativeOffset().top+"px", "left":self.element.cumulativeOffset().left+self.element.getWidth()+"px" }); } // vartemp=self; while(temp){ temp.open(); temp=temp.parent; } //self.childMenu.show(); self.menu.currentItem=self; } this.element.observe("mouseover",elementMouseOver.bindAsEventListener()); functionelementMouseOut(e){ //关闭当前的子菜单 self.timeoutClose(); } this.element.observe("mouseout",elementMouseOut.bindAsEventListener()); this.childMenu=$(document.createElement("ul")); this.childMenu.setStyle({ "position":"absolute", "display":"none", "top":"0px", "left":"0px", "margin":"0px" }); if(this.menu.childMenuClassName) this.childMenu.addClassName(this.menu.childMenuClassName); functionchildMenuMouseOver(e){ vartarget=e.element(); varrelatedTarget=e.relatedTarget||e.fromElement; if(!$(relatedTarget).descendantOf(self.childMenu)&&$(relatedTarget)!=self.childMenu){ self.clearCloseTimeout(); } } this.childMenu.observe("mouseover",childMenuMouseOver.bindAsEventListener()); functionchildMenuMouseOut(e){ vartarget=e.element(); varrelatedTarget=e.relatedTarget||e.toElement; if(!$(relatedTarget).descendantOf(self.childMenu)&&$(relatedTarget)!=self.childMenu){ //关闭所有的菜单 vartemp=self; while(temp){ temp.timeoutClose(); temp=temp.parent; } } } this.childMenu.observe("mouseout",childMenuMouseOut.bindAsEventListener()); $A(this.items).each(function(item,index){ item.render(); varli=$(document.createElement("li")); li.appendChild(item.element); self.childMenu.appendChild(li); }); if(!this.items) return; document.body.appendChild(this.childMenu); }, open:function(){ this.clearCloseTimeout(); if(this.childMenu) this.childMenu.show(); }, close:function(){ if(this.childMenu) this.childMenu.hide(); }, closeAll:function(){ this.close(); if(!this.items)return; this.items.each(function(item){ item.closeAll(); }); }, clearCloseTimeout:function(){ clearTimeout(this.closeTimeoutId); this.closeTimeoutId=null; }, timeoutClose:function(){ varself=this; this.clearCloseTimeout(); this.closeTimeoutId=setTimeout(close,500);//这里不能直接用this.close或者self.close functionclose(){ self.close(); } } });
使用方法
functioncreateItemElement(text){ vara=document.createElement("a"); a.href="javascript:void(0);"; vartextNode=document.createTextNode(text); a.appendChild(textNode); returna; } varmenu=newMenu("childMenu"); //item1 varitem1=newMenuItem($("item1")); menu.addItem(item1); varitem11=newMenuItem(createItemElement("item1-1")); item1.addItem(item11); varitem12=newMenuItem(createItemElement("item1-2")); item1.addItem(item12); // varimg=newImage(); // img.src="http://edu.cnzz.cn/NewsInfo/botton_gif_080.gif"; // vara=document.createElement("a"); // a.href="javascript:void(0);"; // a.appendChild(img); varitem13=newMenuItem(createItemElement("item1-3")); item1.addItem(item13); varitem131=newMenuItem(createItemElement("item1-3-1")); item13.addItem(item131); varitem1311=newMenuItem(createItemElement("item1-3-1-1")); item131.addItem(item1311); varitem132=newMenuItem(createItemElement("item1-3-2")); item13.addItem(item132); varitem14=newMenuItem(createItemElement("item1-4")); item1.addItem(item14); varitem141=newMenuItem(createItemElement("item1-4-1")); item14.addItem(item141); varitem1411=newMenuItem(createItemElement("item1-4-1-1")); item141.addItem(item1411); varitem142=newMenuItem(createItemElement("item1-4-2")); item14.addItem(item142); //item2 varitem2=newMenuItem($("item2")); menu.addItem(item2); varitem21=newMenuItem(createItemElement("item2-1")); item2.addItem(item21); varitem22=newMenuItem(createItemElement("item2-2")); item2.addItem(item22); varitem23=newMenuItem(createItemElement("item2-3")); item2.addItem(item23); menu.render();
本文作者:未知
源代码网供稿. |