来源:蜘蛛抓取(WebSpider)
时间:2012-02-05 12:13
标签:
该怎么过
使用Popup窗口创建无限级Web页菜单(7)
- birdshome - 博客园
随笔 - 333, 文章 - 870, 评论 - 5754, 引用 - 356
    这一节主要说一下Menu对键盘的支持,本来不支持键盘这个菜单也完全可用了,不过还是为了和WinForm的Menu统一,所以支持了和WinForm菜单一样的操作方式。
    菜单的处理函数Menu.prototype.Keydown是在AttachEvents()方法里通过:
doc.attachEvent('onkeydown', this.Keydown);
来attach的,为什么要使用onkeydown不用onkeypress呢?是为了让菜单通过键盘快捷键来弹出子菜单时和WinForm方式菜单一样。这个doc是该菜单的popup窗口的doucment对象。
    下面一边看代码一边讲吧:
    if ( !evt || !evt.srcElement )
    {
        return;
    }
    var menuBody = evt.srcE
    var menuHtml = FindChildElement(menuBody, 'TABLE');
    if ( !menuHtml || !menuHtml.uniqueId )
    {
        menuHtml = FindParentElement(menuBody, 'TABLE');
        if ( !menuHtml || !menuHtml.uniqueId )
        {
            return;
        }
    }
    var menuObj = __MenuCache__[menuHtml.uniqueId];
    if ( menuObj.HasSubMenuExpanded() )
    {
        return;
    }
    由于onkeydown事件处理函数attach在document上,所以要得到菜单必须寻找body里面的Table element,不过这个evt.srcElement可能是body,也可能是table的里的元素,关键是看当时菜单popup里的焦点在那个element上。上面代码的最后4句话是判断响应onkeydown事件的菜单是否有子菜单expanded,因为我们只让最后一级显示的子菜单处理keystroke,父级的必须忽略,否则就乱套了。
    if ( menuObj.m_ShowTimer )
    {
        window.clearTimeout(menuObj.m_ShowTimer);
        menuObj.m_ShowTimer = null;
    }
    这是用来支持子菜单显示特效的一个timer,如果手动显示子菜单(包括鼠标click和键盘快捷键),清除这个timer。
    var activeIndex = -<span style="COLOR: #;
    for ( var i=<span style="COLOR: # ; i & menuObj.m_Items.length ; ++i )
    {
        if ( menuObj.m_ActiveItem == menuObj.m_Items[i] )
        {
            activeIndex = i;
            break;
        }
    }
    把菜单中已active的item的index搜索出来,没有active的menuitem,index为-1。
    var sign = -<span style="COLOR: #; 
    switch( evt.keyCode )
    {
        case <span style="COLOR: # : // left
        {
            if ( menuObj.m_ParentMenu )
            {
                menuObj.Hide();
            }
            break;
        }
        case <span style="COLOR: # : // up | no 
        {
            sign = <span style="COLOR: #;
            if ( activeIndex == -<span style="COLOR: # )
            {
                activeIndex = <span style="COLOR: #;
            }
        }
        case <span style="COLOR: # : // down
        {
            var itemCount = menuObj.m_Items.
            for ( var i=<span style="COLOR: # ; i &= itemCount ; ++i )
            {
                var index = (itemCount+activeIndex-i*sign)%itemC
                var item = menuObj.m_Items[index];
                if ( !item.m_Disabled && item.m_Text != '-' )
                {
                    menuObj.__resumeItem();
                    menuObj.m_ActiveItem = 
                    menuObj.__activeItem();
                    break;
                }
            }
            break;
        }
        case <span style="COLOR: # : // right | no 
        {
            var activeItem = menuObj.m_ActiveI 
            if ( !activeItem || !activeItem.m_ChildMenu )
            {
                break;
            }
        }
        case <span style="COLOR: # : // enter
        {
            menuObj.Click();
            break;
        }
        case <span style="COLOR: # :
        {
            break;
        }
    }
    处理left, right, up, down四个键,up和down要麻烦些,因为要查找可用的(separator item和disabled item是不可用的,不能被active)下一个itme来active,到了最有一条itme再同方向up或down还需要有轮转的效果。
    HACK: 由于up和down的代码完全相同,只是搜索方向不同,所以用了一个sign(取值1|-1)标志来判断搜索方向。
    if ( evt.keyCode &= <span style="COLOR: # && evt.keyCode &= <span style="COLOR: # )
    {
        var keyList = '';
        var key = String.fromCharCode(evt.keyCode); 
        for ( var i=<span style="COLOR: # ; i & menuObj.m_Items.length ; ++i )
        {
            var item = menuObj.m_Items[i];
            if ( !item.m_Disabled && item.m_Mnemonic )
            { 
                keyList += item.m_M
            }
            else
            {
                keyList += '-';
            }
        }
        var index = keyList.indexOf(key); 
        if ( index != -<span style="COLOR: # )
        { 
            if ( keyList.indexOf(key) == keyList.lastIndexOf(key) )
            {
                if ( !menuObj.m_Items[index].m_Disabled )
                {
                    menuObj.__resumeItem(); 
                    menuObj.m_ActiveItem = menuObj.m_Items[index];
                    menuObj.__activeItem();
                    menuObj.Click();
                }
            }
            else
            {
                menuObj.__resumeItem();
                var newA
                if ( !evt.shiftKey )
                {
                    newActive = keyList.indexOf(key, activeIndex+<span style="COLOR: #);
                }
                else
                {
                    if ( activeIndex == <span style="COLOR: # )
                    {
                        newActive = -<span style="COLOR: #;
                        index = keyList.lastIndexOf(key);
                    }
                    else 
                    {
                        newActive = keyList.lastIndexOf(key, activeIndex-<span style="COLOR: #);
                    }
                }
                     
                if ( newActive == -<span style="COLOR: # )
                {
                    menuObj.m_ActiveItem = menuObj.m_Items[index];
                }
                else
                {
                    menuObj.m_ActiveItem = menuObj.m_Items[newActive];
                }
                menuObj.__activeItem();
            }
        }
    }
    处理菜单条目上的快捷键Mnemonic,这里的算法是这样的,把该菜单上的每个item上的Mnemonic字符取出组成一个字符串,没有Mnemonic就用'-'代替。比如下面的菜单的Mnemonic字符组成的字符串分别是:
    
    第一级:&--N---&,第二级:&M&,第三级:&TTTT&。然后使用String.indexOf(key)就取到被按快捷键的MenuItem的index了,由于没有限制同一个Menu里面多个MenuItem具有相同的Mnemonic,所以像第三级菜单,一直按T键的效果就和按down key一样,它的效果使用语句Sting.indexOf(key, activeIndex+<span style="COLOR: #)来获得。 
附Menu.prototype.Keydown = function(evt)代码#region 附Menu.prototype.Keydown = function(evt)代码 
Menu.prototype.Keydown = function(evt)
    if ( !evt || !evt.srcElement )
    {
        return;
    }
    var menuBody = evt.srcE
    var menuHtml = FindChildElement(menuBody, 'TABLE');
    if ( !menuHtml || !menuHtml.uniqueId )
    {
        menuHtml = FindParentElement(menuBody, 'TABLE');
        if ( !menuHtml || !menuHtml.uniqueId )
        {
            return;
        }
    }
    var menuObj = __MenuCache__[menuHtml.uniqueId];
    if ( menuObj.HasSubMenuExpanded() )
    {
        return;
    }
    if ( menuObj.m_ShowTimer )
    {
        window.clearTimeout(menuObj.m_ShowTimer);
        menuObj.m_ShowTimer = null;
    }
    var activeIndex = -<span style="COLOR: #;
    for ( var i=<span style="COLOR: # ; i & menuObj.m_Items.length ; ++i )
    {
        if ( menuObj.m_ActiveItem == menuObj.m_Items[i] )
        {
            activeIndex = i;
            break;
        }
    }
    var sign = -<span style="COLOR: #; 
    switch( evt.keyCode )
    {
        case <span style="COLOR: # : // left
        {
            if ( menuObj.m_ParentMenu )
            {
                menuObj.Hide();
            }
            break;
        }
        case <span style="COLOR: # : // up | no 
        {
            sign = <span style="COLOR: #;
            if ( activeIndex == -<span style="COLOR: # )
            {
                activeIndex = <span style="COLOR: #;
            }
        }
        case <span style="COLOR: # : // down
        {
            var itemCount = menuObj.m_Items.
            for ( var i=<span style="COLOR: # ; i &= itemCount ; ++i )
            {
                var index = (itemCount+activeIndex-i*sign)%itemC
                var item = menuObj.m_Items[index];
                if ( !item.m_Disabled && item.m_Text != '-' )
                {
                    menuObj.__resumeItem();
                    menuObj.m_ActiveItem = 
                    menuObj.__activeItem();
                    break;
                }
            }
            break;
        }
        case <span style="COLOR: # : // right | no 
        {
            var activeItem = menuObj.m_ActiveI 
            if ( !activeItem || !activeItem.m_ChildMenu )
            {
                break;
            }
        }
        case <span style="COLOR: # : // enter
        {
            menuObj.Click();
            break;
        }
        case <span style="COLOR: # :
        {
            break;
        }
    }
    if ( evt.keyCode &= <span style="COLOR: # && evt.keyCode &= <span style="COLOR: # )
    {
        var keyList = '';
        var key = String.fromCharCode(evt.keyCode); 
        for ( var i=<span style="COLOR: # ; i & menuObj.m_Items.length ; ++i )
        {
            var item = menuObj.m_Items[i];
            if ( !item.m_Disabled && item.m_Mnemonic )
            { 
                keyList += item.m_M
            }
            else
            {
                keyList += '-';
            }
        }
        var index = keyList.indexOf(key); 
        if ( index != -<span style="COLOR: # )
        { 
            if ( keyList.indexOf(key) == keyList.lastIndexOf(key) )
            {
                if ( !menuObj.m_Items[index].m_Disabled )
                {
                    menuObj.__resumeItem(); 
                    menuObj.m_ActiveItem = menuObj.m_Items[index];
                    menuObj.__activeItem();
                    menuObj.Click();
                }
            }
            else
            {
                menuObj.__resumeItem();
                var newA
                if ( !evt.shiftKey )
                {
                    newActive = keyList.indexOf(key, activeIndex+<span style="COLOR: #);
                }
                else
                {
                    if ( activeIndex == <span style="COLOR: # )
                    {
                        newActive = -<span style="COLOR: #;
                        index = keyList.lastIndexOf(key);
                    }
                    else 
                    {
                        newActive = keyList.lastIndexOf(key, activeIndex-<span style="COLOR: #);
                    }
                }
                     
                if ( newActive == -<span style="COLOR: # )
                {
                    menuObj.m_ActiveItem = menuObj.m_Items[index];
                }
                else
                {
                    menuObj.m_ActiveItem = menuObj.m_Items[newActive];
                }
                menuObj.__activeItem();
            }
        }
    }
    if ( evt.keyCode != <span style="COLOR: # )
    {  
        evt.returnValue = null;
        evt.cancelBubble = true; 
    }
     ...