Javascript多级菜单(二)

一、开篇

上次写了一个没有任何效果的多级菜单,有朋友说直接用CSS就可以实现,所以就继续加工了一下,做了两个用CSS不能实现的菜单,效果如下:

渐变多级菜单

滑动多级菜单

二、原理

修改了一下上一篇中的代码,再次总结一下原理:

 

主要是要响应4个事件:菜单项(MenuItem.element,是一个li)的mouseover和mouseout以及子菜单MenuItem.childMenu,是一个ul)的mouseover和mouseout事件。这几种事件都可能因为其子元素的冒泡而重复发生,可以利用上一篇介绍的方法,判断relatedTarget来防止这种事情的发生。

var target = e.element();
var relatedTarget = e.relatedTarget || e.fromElement;
if(!relatedTarget)return;
if($(relatedTarget).descendantOf(self.element) || $(relatedTarget) == self.element)
    
return;
  • MenuItem.element.mouseover事件:鼠标移动到菜单项时,先要关闭同一级的其他菜单,然后要设置当前子菜单的位置,然后打开当前的子菜单,并且需要打开当前菜单之上的所有菜单;
  • MenuItem.element.mouseout事件:关闭当前菜单的子菜单;
  • MenuItem.childMenu.mouseover事件:清除当前菜单及之上的所有菜单的关闭延时,关闭延时是防止鼠标移出菜单过后菜单马上消失;
  • MenuItem.childMenu.mouseout事件:开始关闭当前菜单以及之上的所有菜单的延时;

打开子菜单和关闭子菜单是通过MenuItem的open和close方法来实现的,但是响应上面四个鼠标事件的过程中,可能会反复调用某个菜单的open和close方法,这样给菜单制作带来了很大的麻烦,尤其是对于渐变和滑动的菜单,重复的open和close会导致菜单产生很多怪异的行为。所以,在MenuItem里面添加了closed这个属性来标志当前子菜单的状态。在open和close调用的时候都需要先判断,如果已经打开了就不能再次打开但是可以关闭,反之亦然。

最简单的open和close方法如下:

open:function(){//立即打开当前菜单
    if(!this.isClosed)return;//保证不重复打开
    this.clearCloseTimeout();
    
this.menu.liFocus(this);
    
if(this.childMenu){
        
this.childMenu.show();
        writeLog(
this.name + " childMenu show");
    }
    
this.isClosed = false;
},
close:function(){//立即关闭当前菜单
    if(this.isClosed)return;//保证不重复关闭
    this.clearCloseTimeout();
    
this.menu.liBlur(this);
    
if(this.childMenu){
        
this.childMenu.hide();
        writeLog(
this.name + " childMenu hide");
    }
    
this.isClosed = true;
},

 

 对于渐变菜单和滑动菜单,就可以集中来解决做渐变和滑动效果,其他的变化不是很大(滑动菜单的render不一样)。

对于渐变菜单,就是设置一下透明度:

open:function(){//立即打开当前菜单
    this.clearCloseTimeout();
    
if(!this.isClosed)return;//保证不重复打开
    this.menu.liFocus(this);
    
if(this.childMenu){
        clearInterval(
this.fadeInIntervalId);
        clearInterval(
this.fadeOutIntervalId);
        
var self = this;
        
var init = 0;
        
this.childMenu.setOpacity(init);
        
this.childMenu.show();
        
function fadeIn(){
            init 
+= 0.1;
            
if(init >= 1)
                init 
= 1;
            self.childMenu.setOpacity(init);
            
if(init == 1){
                clearInterval(self.fadeInIntervalId);
                
//self.isClosed = false;
            }
        }
        
this.fadeInIntervalId = setInterval(fadeIn,30);
        writeLog(
this.name + " childMenu show");
    }
    
this.isClosed = false;
}

close:function(){//立即关闭当前菜单
    this.clearCloseTimeout();
    
if(this.isClosed)return;//保证不重复关闭
    this.menu.liBlur(this);
    
if(this.childMenu){
        clearInterval(
this.fadeInIntervalId);
        clearInterval(
this.fadeOutIntervalId);
        
var self = this;
        
var init = 1;
        
this.childMenu.setOpacity(init);
        
function fadeOut(){
            init 
-= 0.1;
            
if(init <= 0)
                init 
= 0;
            self.childMenu.setOpacity(init);
            
if(init == 0){
                clearInterval(self.fadeOutIntervalId);
                self.childMenu.hide();
            }
        }
        
this.fadeOutIntervalId = setInterval(fadeOut,10);
        writeLog(
this.name + " childMenu hide");
    }
    
this.isClosed = true;
}

 对于滑动菜单,原理可以参看以前的介绍滑动菜单的文章:滑动菜单(一)滑动菜单(二)

滑动菜单的open和close如下:

 

open:function(){//立即打开当前菜单
    this.clearCloseTimeout();
    
if(!this.isClosed)return;//保证不重复打开
    this.menu.liFocus(this);
    
if(this.childMenu){
        clearInterval(
this.slideInIntervalId);
        clearInterval(
this.slideOutIntervalId);
        
this.childMenuContainer.show();
        
var self = this;
        
var start = 0;
        
if(this.depth == 0)
            start 
= parseInt(this.childMenu.getStyle("top"));
        
else
            start 
= parseInt(this.childMenu.getStyle("left"));
        
        
var end = 0;
        
function slideIn(){
            
var step = Math.round((end - start)/5);
            start += step;
            
if(start >= end || step == 0)
                start 
= end;
            
if(self.depth == 0)
                self.childMenu.setStyle({
                    
"top":start + "px"
                });
            
else
                self.childMenu.setStyle({
                    
"left":start + "px"
                });
            
if(start == end){
                clearInterval(self.slideInIntervalId);
                self.isClosed 
= false;
            }
        }
        
this.slideInIntervalId = setInterval(slideIn,15);
        writeLog(
this.name + " childMenu show");
    }
    
this.isClosed = false;
}

close:function(){//立即关闭当前菜单
    this.clearCloseTimeout();
    
if(this.isClosed)return;//保证不重复关闭
    this.menu.liBlur(this);
    
if(this.childMenu){
        clearInterval(
this.slideInIntervalId);
        clearInterval(
this.slideOutIntervalId);
        
var self = this;
        
var start = 0;
        
if(this.depth == 0)
            start 
= parseInt(this.childMenu.getStyle("top"));
        
else
            start 
= parseInt(this.childMenu.getStyle("left"));
            
        
var end = 0;
        
if(this.depth == 0)
            end 
= -this.childMenu.getHeight();
        
else
            end 
= -this.childMenu.getWidth();
        
        
function slideOut(){
            
var step = Math.round((start - end)/5);
            start -= step;
            
if(start <= end || step == 0)
                start 
= end;
            
            
if(self.depth == 0)
                self.childMenu.setStyle({
                    
"top":start + "px"
                });
            
else
                self.childMenu.setStyle({
                    
"left":start + "px"
                });
            
if(start == end){
                clearInterval(self.slideOutIntervalId);
                
//self.isClosed = false;
                self.childMenuContainer.hide();
            }
        }
        
this.slideOutIntervalId = setInterval(slideOut,30);
        writeLog(
this.name + " childMenu hide");
    }
    
this.isClosed = true;
}

三、示例下载

       点此下载示例

posted @ 2008-11-08 23:50  LongWay  阅读(5275)  评论(6编辑  收藏  举报