/*
*  jQuery tui tuihotkey plugin 0.3
*
*  Copyright (c) 2010 china
*
* Dual licensed under the MIT and GPL licenses:
*   http://www.opensource.org/licenses/mit-license.php
*   http://www.gnu.org/licenses/gpl.html
*
*
* Create: 2010-10-21 11:33:15 yewf $
* Revision: $Id: tui.hotkey.js  2010-02-18 17:44:23 yewf $ 
*
* Notes:1、IE环境下alt键会聚焦菜单,因此尽量不要使用alt键,目前可选ctrl,shift,alt,但由于键盘布局,目前使用了alt键,在IE环境下需要按两次alt键;
*       2、html tag标签id必须区分大小写;
*       3、由于使用了document.click与keydown事件,因此存件事件冒泡问题;
*       4、多键请使用半角逗号分隔;
*       5、只能支持两个按键;
*       6、由于$("#obj").height()的值,同一个UL下的li都会有不一样的情况,
*          因此无法准确设置top值,解决方法是给每个key增加一个top属性,用过手动定义top值。默认使用keyStyle.top值。 
*          keyTop = $("#obj").offset().top + key.top;
*       7、最后一个按键必须定义event,否则会提示未定义event;
*       8、不能使用一个JS进行全局定义,必须分页面定义。原因如下:
*           (1)跳转冲突,例如:一级菜单,公司管理,系统管理需要互相跳转,就需要定义跳转事件,
*              如果当前停在公司管理,而又执行了事件跳转,在显示二级菜单热键时,就会降低用户体验。
*           (2)使用全局定义,如果定义有几百行,会导致每个页面下载的数据过大;
*           (3)对于某个页面来说,全局定义有跟本页面无关的定义;
* Sample:
*        $(function() {
*            $.tuihotkey({
*                "primaryId": 0,
*                "keyStyle": {
*                   "top": 20,
*                   "align": "center"
*                },
*                "keys": [
*                 { "key": "a", "id": 1, "pid": 0, "objId": "menu0", "event": function() { goMyoffice(); } },
*                 { "key": "b,o", "id": 2, "pid": 0, "objId": "menu1", "event": function() { goOrder(); } },
*                 { "key": "c", "id": 3, "pid": 0, "objId": "menu2", "event": function() { goGoods(); } },
*                 { "key": "d", "id": 7, "pid": 0, "objId": "menu7" },
*                 { "key": "g", "id": 700, "pid": 7, "objId": "Company0" },
*                 { "key": "j", "id": 701, "pid": 7, "objId": "Company1" },
*                 { "key": "c", "id": 702, "pid": 7, "objId": "Company2" },
*                 { "key": "k", "id": 703, "pid": 7, "objId": "Company3" },
*                 { "key": "a", "id": 7001, "pid": 700, "objId": "liTest1", top: 70, "event": function() { org_Add(); } },
*                 { "key": "b", "id": 7002, "pid": 700, "objId": "liTest2", top: 10, "event": function() { testTT2(); } },
*                 { "key": "c", "id": 7003, "pid": 700, "objId": "liTest3", top: 10, "event": function() { testTT3(); } }
*               ]
*            });
*        });
*
*
*/jQuery.tuihotkey = function(options) {
    var defaults = {
        "primaryKey": "alt",
        "primaryId": 0,
        "keyStyle": {
            "top": 20,
            "align": "center"
        },
        "focusId": null,
        "keys": null
    };    var options = jQuery.extend({}, defaults, options);    var HOTKEY_PRESS = false;  // 如果是hotkey跳转,则为true,否则为false。
    var HOTKEY_WIDTH = 20;
    var HOTKEY_HEIGHT = 20;
    var KEY =
    {
        ALT: 18,
        CTRL: 17,
        SHIFT: 16,
        ESC: 27,
        C: 67,
        V: 86
    };    // 存储在Cookie中的按键,格式: { "key": "17", "id": 0, "pid": 0 } 或者 { "key": "65,66", "id": 1, "pid": 0}
    var _objDIV = new Array();       // 存储显示层的ID号
    var _objFirstKey = new Array();  //第一次按键
    // 热键进行页面跳转时,热键必须保持状态
    init();    jQuery(document).keydown(function(e) {
        var event = window.event || e;
        var pressKey = event.keyCode;        if (KEY.ESC === pressKey) {
            clearHotkey(true);
        }
        else if ((KEY.SHIFT === pressKey && options.primaryKey.toUpperCase() === "SHIFT")
                            || (KEY.CTRL === pressKey && options.primaryKey.toUpperCase() === "CTRL")
                            || (KEY.ALT === pressKey && options.primaryKey.toUpperCase() === "ALT")) {
            // 只要使用了快捷键,就将当前焦点从当前控件移开(比如当前是textbox时,保证输入的热键不会在textbox中出现)
            //alert("您正在使用快捷键提示功能,请参照提示使用");
            if ($.browser.msie)
                jQuery(document.body).focus();
            else {
                // 其它浏览器的情况下,无法使用document.body.focus()
                // 先用尝试使用配置的id号,然后再使用div,再尝试使用table的focus()
                var d;
                if (options.focusId != null) {
                    d = jQuery("#" + options.focusId);
                }
                else {
                    d = $("div:first");
                    if (d.length == 0) {
                        d = jQuery("table:frist");
                    }
                }
                d.attr("tabindex", "9999").css("outline", "none");
                d.focus();
            }            firstKeyPress(pressKey);
        }
        else {
            processKeyPress(pressKey); ;
        }
    })
    .keyup(function(e) {
        // 这里对firefox有效,对ie无效。
        _stopBubble(e);
    })
    .click(function() {
        clearHotkey(true);
    });    // 页面离开时
    jQuery(window).unload(function() {
        // 如果不是用热键离开的页面,则清除cookie。
        if (HOTKEY_PRESS === false) {
            deleteCookie();
        }
    });    // 页面载入时,承接上一页的按键提示
    function init() {
        // 最后一次按钮
        processKeyPress();
    };    // 第一次按键操作,功能键
    function firstKeyPress(keycode) {
        clearHotkey(true);        _objFirstKey = new Array();        // 处理keys
        var objCurrent = keysByPID(options.primaryId);
        if (objCurrent.length === 0) return;        jQuery.each(objCurrent, function(i, n) {
            displayHotkey(n);
        });        // 做了热键提示,则保存alt键
        if (objCurrent.length > 0) {
            // 保存Cookie
            var t = "{ \"key\": \"" + keycode + "\", \"id\": 0, \"pid\": 0 }";
            setCookie(t);
        }
    };    // 处理其它按键操作,字母键
    function processKeyPress(keycode) {
        // 如果是功能键,则不处理
        if (keycode == KEY.CTRL || keycode == KEY.ALT || keycode == KEY.SHIFT) {
            return;
        }        // 最后一次按键
        var lastKey = getCookieObject();
        if (lastKey === null) {
            return;
        }        var lastKeyCode, lastKeyId,
        hotkeyPageRedirect = true; //该参数在使用热键跳转时,不执行热键事件。        if (typeof keycode === "undefined") {
            // 页面跳转后的初始化热键显示
            lastKeyCode = lastKey.key;
            lastKeyId = lastKey.pid; // 这里使用pid,才能找到上一次的按钮定义
            hotkeyPageRedirect = false;
        }
        else {
            // 当前页
            lastKeyCode = keycode;
            lastKeyId = lastKey.id; // 没有跳转,使用上次按键的id查找下一级按键定义            // 第一次按错误的,第二次按正确的。必须再次判断
            var fk = findKey(String.fromCharCode(lastKeyCode), lastKeyId);
            if (fk === "none") {
                if (_objFirstKey.length > 0) {
                    lastKeyCode = _objFirstKey.pop() + "," + lastKeyCode;
                }
            }
        }        // 将逗号分隔的按钮进行字符转换,如68,67转换为D,C
        var keyary = lastKeyCode.toString().split(",");
        var keych = "";        for (var j = 0; j < keyary.length; j++) {
            if (keych === "") {
                keych = String.fromCharCode(keyary[j]);
            }
            else {
                keych = keych + "," + String.fromCharCode(keyary[j]);
            }
        }        // 查找当前按键是否定义
        var curkey = findKey(keych, lastKeyId);        if (curkey === "none") {
            // 支持两个按钮,第一个按键找不到,则有可能是两个按键定义。
            _objFirstKey.push(keycode);
            return;
        }
        else if (curkey === "repeat") {
            alert("Error:\nCan not repeat definition of hotkeys at the same level.");
            return;
        }        // 如果当前key的父对象为空或隐藏,执行到对应的热键时,不执行事件。
        var objTarget = jQuery("#" + curkey.objId);
        if (objTarget.length === 0 || objTarget.is(":hidden")) {
            return;
        }        var t = "{ \"key\": \"" + lastKeyCode + "\", \"id\": " + curkey.id + ", \"pid\": " + curkey.pid + " }";        // 返回符合pid条件的数组
        var objCurrent = keysByPID(curkey.id);
        if (objCurrent.length === 0) {
            // 如果没有子节点,则必须有事件执行,否则按键就没有意义
            if (typeof curkey.event === "undefined" || curkey.event === null) {
                alert("Error:\nLast key must be define [event] argument,please check [\"key\": \"" + curkey.key + "\"],[ \"id\": " + curkey.id + "]。");
                return;
            }            // 保存最后一次按键
            setCookie(t);            if (hotkeyPageRedirect) {
                // 清除提示层,并执行事件
                clearHotkey(false);                HOTKEY_PRESS = true;
                _objFirstKey = new Array();                curkey.event();
            }
            return;
        }        // 移除上一次的按键提示层
        clearHotkey(false);
        // 保存最后一次按键
        setCookie(t);
        // 执行事件
        curkey.event && curkey.event();        // 显示按键
        jQuery.each(objCurrent, function(i, n) {
            displayHotkey(n);
        });        // 清除储存多键的数组。
        _objFirstKey = new Array();
    };    

解决方案 »

  1.   


    // 显示热键提示层(div)
        function displayHotkey(term) {
            var keyName = term.key, objId = term.objId;        var objTag = jQuery("#" + objId);
            if (objTag.length === 0 || objTag.is(":hidden")) return; // throw new Error("Final Error:Html Object [" + objId + "] not exist, the application is not work.");        var t = objTag.offset().top;
            var l = objTag.offset().left;
            var w = objTag.width();
            var padl = jQuery.browser.mozilla ? "1" : "0";        if (term.top) {
                t = t + term.top;
            }
            else {
                t = t + options.keyStyle.top;
            }        // 根据个数计算宽度
            var cc = keyName.split(",").length;
            //var kwidth = cc === 1 ? HOTKEY_WIDTH : cc * HOTKEY_WIDTH * 0.6;//动态宽度
            var kwidth = HOTKEY_WIDTH;        if (options.keyStyle.align === "center") {
                l = l + w / 2 - kwidth / 2;
            }
            var tagid = objId + "_hotkey_" + keyName.substr(0, 1);
            var tagdiv = jQuery("#" + tagid);
            if (tagdiv.length === 0) {
                jQuery("<div/>")
                .attr("id", tagid)
                .text(keyName.replace(/\,/g, "").toUpperCase())
                .css({
                    "top": t + "px",
                    "left": l + "px",
                    "position": "absolute",
                    "width": kwidth + "px",
                    "height": HOTKEY_HEIGHT + "px",
                    "text-align": "center",
                    "font-weight": "bold",
                    "padding-left": padl + "px",
                    "z-index": 999999
                })
                .addClass("tui-hotkey-bg")
                .appendTo(document.body);            // 将当前显示的div存储,以便移除操作。
                _objDIV.push(tagid);
            }
        };    // 移除热键提示层,并且传入是否移除Cookie的bool值。
        function clearHotkey(isDeleteCookie) {
            jQuery.each(_objDIV, function(i, n) {
                jQuery("#" + n).remove();
            });        if (isDeleteCookie) {
                deleteCookie();
            }        // 重置显示层与当前按键key数组。
            _objDIV = new Array();
        };    // 返回符合pid条件的key
        function keysByPID(pid) {
            var temp = new Array();
            jQuery.each(options.keys, function(i, n) {
                if (n.pid === pid) {
                    temp.push(n);
                }
            });        return temp;
        };    // 根据key与pid查找key对象,只返回第一个符合条件的,如果是两个就说明同层进行了重复定义。
        function findKey(key, pid) {
            var temp, c = 0;
            jQuery.each(options.keys, function(i, n) {
                if (n.pid === pid && n.key.toUpperCase() === key.toUpperCase()) {
                    temp = n;
                    c++;
                }
            });
            if (c === 0) return "none";
            if (c > 1) return "repeat";
            return temp;
        };    // Cookie操作,调用jquery.cookie.js
        function setCookie(value) {
            _cookie("tuihotkey", value, { path: '/' });
        };    // 返回保存的按键数组
        function getCookieObject() {
            if (_cookie("tuihotkey") != null) {
                if (_cookie("tuihotkey") === "") {
                    return null;
                }
                return eval("(" + _cookie("tuihotkey") + ")");
            }
            return null;
        };    // 删除cookie
        function deleteCookie() {
            _cookie("tuihotkey", null, { path: '/' });
        };    // 从jquery.cookie.js中复制过来的cookie操作
        function _cookie(name, value, options) {
            if (typeof value != 'undefined') { // name and value given, set cookie
                options = options || {};
                if (value === null) {
                    value = '';
                    options = jQuery.extend({}, options); // clone object since it's unexpected behavior if the expired property were changed
                    options.expires = -1;
                }
                var expires = '';
                if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
                    var date;
                    if (typeof options.expires == 'number') {
                        date = new Date();
                        date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
                    } else {
                        date = options.expires;
                    }
                    expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
                }
                // NOTE Needed to parenthesize options.path and options.domain
                // in the following expressions, otherwise they evaluate to undefined
                // in the packed version for some reason...
                var path = options.path ? '; path=' + (options.path) : '';
                var domain = options.domain ? '; domain=' + (options.domain) : '';
                var secure = options.secure ? '; secure' : '';
                document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
            } else { // only name given, get cookie
                var cookieValue = null;
                if (document.cookie && document.cookie != '') {
                    var cookies = document.cookie.split(';');
                    for (var i = 0; i < cookies.length; i++) {
                        var cookie = jQuery.trim(cookies[i]);
                        // Does this cookie string begin with the name we want?
                        if (cookie.substring(0, name.length + 1) == (name + '=')) {
                            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                            break;
                        }
                    }
                }
                return cookieValue;
            }
        };    //阻止浏览器的默认行为
        function _stopDefault(e) {
            //阻止默认浏览器动作(W3C) 
            if (e && e.preventDefault)
                e.preventDefault();
            //IE中阻止函数器默认动作的方式 
            else
                window.event.returnValue = false;
            return false;
        };    function _stopBubble(e) {
            //如果提供了事件对象,则这是一个非IE浏览器 
            if (e && e.stopPropagation)
            //因此它支持W3C的stopPropagation()方法 
                e.stopPropagation();
            else
            //否则,我们需要使用IE的方式来取消事件冒泡 
                window.event.cancelBubble = true;
        };
    };
    需要交流请到如下博客地址,谢谢。
    http://blog.csdn.net/SaRoot/archive/2011/03/03/6220412.aspx