前面的(​​1​​),(​​2​​),(​​3​​)解决了popup创建Menu的主要技术问题后,现在开始具体的编码心里就有底多了,而且可以把精力集中在逻辑的处理上。当然还有一些UI的问题需要考虑,但都是HTML+CSS的小问题了。


    菜单的数据结构其实就是树,由于Menu及MenuItem有很多的自身属性,我们使用面向对象的方式来实现这个菜单。关于JavaScript面向对象的编成不是我讨论的主题,可以参考​​蓝色经典​​上的文章来了解。我们一共实现两个类,一个Menu和一个MenuItem,Menu相当于一个集合,里面容纳n个MenuItem,其上除了菜单的属性外,主要是操作Collection的方法。类的定义如下:


function Menu()

{

    this.m_Items = [];                  // 菜单条目集合

    this.m_Popup = null;            // 显示菜单的popup窗口

    this.m_Invalidate = false;     // 是否失效标志

    this.m_Drawn = false;          // 菜单是否已输出

    this.m_Opener = null;           // 菜单的父窗口window对象

    this.m_ParentMenu = null;  // 菜单的父菜单MenuItem对象

    this.m_ActiveItem = null;     // 被激活(highlighting)的MenuItem

    this.m_ShowTimer = null;   // 鼠标停留在有子菜单的条目上,子菜单显示延迟计时器

    this.m_Bounds = null;          // 菜单的bounds

    this.m_ShowHeaderBlank = true; // 是否显示MenuItem前的空白区域

    this.m_IsEventAttached = false;    // 事件是否attached

    this.m_Id = __MenuCache__.NewId(); // 或取菜单对象的唯一标识

    __MenuCache__[this.m_Id] = this; // 把菜单放入__MenuCache__

    this.toString = function()

    {

        return '[class Menu]';

    };

}


    菜单类的方法有:


Menu.prototype.Add = function(mi)

{

    // 添加菜单条目到菜单中

};

Menu.prototype.AddAt = function(mi, index)

{

    // 把菜单条目添加到指定的数组索引上

};

Menu.prototype.AddSeparator = function()

{

    // 添加一个Separator Item,就是我们在window菜单里的"-"

};

Menu.prototype.Remove = function(mi)

{

    // 删除菜单中的一个菜单条目

};

Menu.prototype.Contains = function(menu)

{

    // 判断已构建的菜单中是否已包含了menu

};

Menu.prototype.Render = function()

{

    // 生成菜单UI显示需要的DHTML

};

Menu.prototype.__generatePaddingTR = function(doc)

{

    // 为了美化菜单UI生成的一个TR element

};

Menu.prototype.AttachEvents = function(menu)

{

    // attach事件处理函数到菜单事件上

};

Menu.prototype.ActiveItem = function(evt)

{

    // 处理菜单itme被Active后的UI和动作等

};

Menu.prototype.Hide = function()

{

    // 隐藏菜单

};

Menu.prototype.Keydown = function(evt)

{

    // 处理键盘按键

};

Menu.prototype.Click = function()

{

    // 执行菜单被click

};

Menu.prototype.ResumeItem = function(evt)

{

    // 恢复菜单,取消active和恢复UI

};

Menu.prototype.__resumeItem = function()

{

    // 执行UI恢复

};

Menu.prototype.__resumeAll = function()

{

    // 执行批量UI恢复

};

Menu.prototype.__activeItem = function()

{

    // 执行UI激活

};

Menu.prototype.HasSubMenuExpanded = function()

{

    // 判断菜单是否有展开的submenu

};

Menu.prototype.__isEllipsis = function(menuObj, menuHtml)

{

    // 在菜单item的text过长时将截断并显示""

};

Menu.prototype.Show = function(evt)

{

    // 显示菜单

};

Menu.prototype.InnerShow = function()

{

    // 显示submenu,用于菜单内部触发的菜单显示

};

Menu.prototype.__show = function(miObj)

{

    // 执行菜单显示

};

Menu.prototype.FadeinEffect = function(effect)

{

    // 菜单显示式的特效,是用filter来实现,只在Show菜的时候调用

};


    类MenuItem比Menu类简单很多,定义如下:


function MenuItem(text, action, icon, shortcut, menu)

{

    this.m_Text = text;                // 菜单文本

    this.m_Action = action;        // 菜单条目被触发时执行的函数

    this.m_Icon = icon;               // 菜单条目前的图标路径

    this.m_ChildMenu = menu; // 子菜单,类型为Menu对象

    this.m_Menu = null;              // 本菜单条目所在的菜单对象实例

    this.m_ShortCut = shortcut; // 快捷方式(保留,未实现)

    this.m_Disabled = false;      

    this.m_Mnemonic = null;

    this.m_Tooltip = null;

    this.m_Attributes = [];          // 附加属性集合,由SetAttribute设置

    this.m_Id = __MenuCache__.NewId();

    __MenuCache__[this.m_Id] = this;

    this.toString = function()

    {

        return '[class MenuItem]';

    };

}


    MenuItem类的方法如下:


MenuItem.prototype.Contains = function(menu)

{

    // 子菜单中是否已添加menu

};

MenuItem.prototype.SetAttribute = function(key, value)

{

    // 设置用户定义的属性

};

MenuItem.prototype.GetAttribute = function(key)

{

    // 或取用户定义的属性

};

MenuItem.prototype.Invalidate = function()

{

    // 失效

};

MenuItem.prototype.IsSeparator = function()

{

    // 判断MenuItem是否为Separator,就是其m_Text == '-'

}

MenuItem.prototype.Render = function()

{

    // 生成菜单UI的DHTML

};

MenuItem.prototype.SetBorderColor = function(miHtml, width, borderColor)

{

    // 设置菜单条目的边框颜色

};

    "__"开头的方法是内部方法,不提供给类外使用。变量命名规则m_开头的是类属性变量,Obj结尾的是菜单类对象,和它相对的是Html结尾的,使菜单的HTML元素对象,常见的是menuObj、menuHtml、miObj和miHTML。



    Menu类中最重要的方法是:

Menu.prototype.AttachEvents = function(menu)

Menu.prototype.ActiveItem = function(evt)

Menu.prototype.HasSubMenuExpanded = function()

Menu.prototype.Show = function(evt)

Menu.prototype.InnerShow = function()

Menu.prototype.__show = function(miObj)

    MenuItem主要是处理UI显示,没有太重要的方法。