资讯专栏INFORMATION COLUMN

基于 jQuery 的键盘事件监听控件

付伦 / 549人阅读

摘要:最近项目里要做一个画板,需要对键盘事件进行监听,来进行诸如撤回重做移动缩放等快捷键操作,因此顺手实现了一个键盘事件监听控件,略有收获,整理出来,希望对大家有所帮助,更希望能获得高手的指点。

最近项目里要做一个画板,需要对键盘事件进行监听,来进行诸如撤回、重做、移动、缩放等快捷键操作,因此顺手实现了一个键盘事件监听控件,略有收获,整理出来,希望对大家有所帮助,更希望能获得高手的指点。

1. 自动获取焦点

似乎浏览器的键盘事件只能被那些可以获得焦点的元素设置监听,而通常需要监听事件的

元素都不能获得焦点,因此需要修改目标元素的某些属性使其可以获得焦点,另外一种可行的方法是将事件委托给诸如 标签。这里采用的是第一类方法,当然,可以修改的属性也不止一种,例如,对于
标签可以将其 “editable” 属性设为 true,而这里采用的是给其设一个 tabindex 值。代码如下:

        $ele.attr("tabindex", 1);     

另外,焦点事件的触发需要点击元素或者 TAB 切换,而这并不符合人类的直觉,因此需要监听鼠标移入事件,使目标元素“自动”地获得焦点:

$ele.on("mouseenter", $ele.focus);
2. 监听键盘事件

由于项目面向的客户所使用的浏览器以chrome为主(实际上是36x浏览器),因此没有针对浏览器做任何适配,仅仅使用了 jQuery的事件监听:

        $ele.on("keydown", this._keyDownHandler.bind(this));
        

由于实现是控件化的,所以定义了一个私有方法 _keyDownHandler 来响应键盘的动作。

3. 按键事件甄别

jQuery事件监听器返回的事件对象信息较多,因此需要进行甄别,为此定义了一个私有方法 _keyCodeProcess 来处理按键

function _keyCodeProcess(e){
        var code = e.keyCode + "";
        var altKey = e.altKey;
        var ctrlKey = e.ctrlKey;
        var shiftKey = e.shiftKey;

        var threeKey = altKey && ctrlKey && shiftKey;
        var ctrlAlt = altKey && ctrlKey;
        var altShift = altKey && shiftKey;
        var ctrlShift = shiftKey && ctrlKey;

        var keyTypeSet = this.keyTypeSet;
        var resStr = "";

        if(threeKey){
            resStr = keyTypeSet.threeKey[code];
        } else if(ctrlAlt) {
            resStr = keyTypeSet.ctrlAlt[code];
        } else if(ctrlShift) {
            resStr = keyTypeSet.ctrlShift[code];
        } else if(altShift) {
            resStr = keyTypeSet.altShift[code];
        } else if(altKey) {
            resStr = keyTypeSet.altKey[code];
        } else if(ctrlKey) {
            resStr = keyTypeSet.ctrlKey[code];
        } else if(shiftKey) {
            resStr = keyTypeSet.shiftKey[code];
        } else {
            resStr = keyTypeSet.singleKey[code];
        }

        return resStr
    };
    

这里的 keyTypeSet 是一个类似于查找表的对象,里面存储了 ctrl、shift、alt按钮的各种类型组合,每种组合下又分别按照按键码存储一个自定义事件类型字符串,事件发生之后会从这里返回这个字符串,当然,没有对应自定义事件的时候,就老老实实地返回空字符串。

4. 事件分发

_keyCodeProcess 方法从事件中提取出了事件类型,我们提前将监听的回调函数存储在一个查找表 callback 中,并且“巧妙”地使得其键名刚好为自定义事件字符串前面加个“on”前缀,就可以方便地调用了,前述 _keyDownHandler 正是为此而设计的:

function _keyDownHandler(e){
        var strCommand = this._keyCodeProcess(e);

        var objEvent = {
            type: "",
            originEvent: e.originEvent
        };

        strCommand && this.callback["on" + strCommand](objEvent);

        return null;
    };
    
    
5. 事件订阅与解除订阅

前面说了,我们是把回调函数存储起来适时调用的,因此需要对外暴露一个“订阅”接口,让开发者可以方便地把自己的回调函数存储到对象实例中去,为此,我定义了一个 .bind接口:

function bind(type, callback, description){
        var allType = this.allEventType;
        if(allType.indexOf(type) === -1){
            throwError("不支持的事件类型,请先扩展该类型,或采用其他事件类型");
        }

        if(!(callback instanceof Function)){
            throwError("绑定的事件处理回调必须是函数类型");
        }

        this.callback["on" + type] = callback;

        this.eventDiscibeSet[type] = description || "没有该事件的描述";

        return this;
    };
    

由于是给人用的,所以顺带做了下类型检查。
根据接口的“对称性”,有订阅最好也有解除订阅,因此定义了 .unbind接口,只有一句代码,实现如下:

function unbind(type){
        this.callback["on" + type] = this._emptyEventHandler;

        return this;
    };
    
6.扩展自定义事件类型

键盘事件的组合丰富多彩,如果全部内置在控件中的话,会是很臃肿的,因此除了少数几个常见的组合键之外,开发者可以通过 .extendEventType 方法,来自定义组合键和返回的字符串:

function extendEventType(config){
        var len = 0;
        if(config instanceof Array){
            len = config.length;
            while(len--){
                this._setKeyComposition(config[len]);
            }
        } else {
            this._setKeyComposition(config);
        }
        return this;
    };
    

其中的 ._setKeyComposition 是一个私有方法,用来写入自定义键盘事件:

function _setKeyComposition(config){
        var altKey = config.alt;
        var ctrlKey = config.ctrl;
        var shiftKey = config.shift;

        var threeKey = altKey && ctrlKey && shiftKey;
        var ctrlAlt = altKey && ctrlKey;
        var altShift = altKey && shiftKey;
        var ctrlShift = shiftKey && ctrlKey;
        var code = config.code + "";

        if(threeKey){
            this.keyTypeSet.threeKey[code] = config.type;
        } else if(ctrlAlt) {
            this.keyTypeSet.ctrlAlt[code] = config.type;
        } else if(ctrlShift) {
            this.keyTypeSet.ctrlShift[code] = config.type;
        } else if(altShift) {
            this.keyTypeSet.altShift[code] = config.type;
        } else if(altKey) {
            this.keyTypeSet.altKey[code] = config.type;
        } else if(ctrlKey) {
            this.keyTypeSet.ctrlKey[code] = config.type;
        } else if(shiftKey) {
            this.keyTypeSet.shiftKey[code] = config.type;
        } else {
            this.keyTypeSet.singleKey[code] = config.type;
        }
        
        this.allEventType.push(type);
        this.callback["on" + type] = this._emptyEventHandler;

        return null;
    };
    

这样,一个键盘事件监听控件就大功告成了,下面是完整实现代码:

/**
 * @constructor 键盘事件监听器
 * */
function KeyboardListener(param){
    this._init(param);
}

!function(){
    "use strict";

    /**
     * @private {String} param.ele 事件对象选择器
     * */
    KeyboardListener.prototype._init = function _init(param){
        this.$ele = $(param.ele);

        this._initEvents();

        return null;
    };

    /**
     * @private _emptyEventHandler 空白事件响应
     * */
    KeyboardListener.prototype._emptyEventHandler = function _emptyEventHandler(){
        return null;
    };

    /**
     * @private _initEvents 绑定 DOM 事件
     * */
    KeyboardListener.prototype._initEvents = function _initEvents(){
        this.allEventType = [];
        this.callback = {};
        this.eventDiscibeSet = {};

        var $ele = this.$ele;

        $ele.attr("tabindex", 1);

        $ele.on("mouseenter", function(){
            $ele.focus();
        });

        $ele.on("keydown", this._keyDownHandler.bind(this));

        this.keyTypeSet = {
            altKey: {},
            ctrlAlt: {},
            ctrlKey: {},
            threeKey: {},
            altShift: {},
            shiftKey: {},
            ctrlShift: {},
            singleKey: {}
        };

        // 支持一些内建的键盘事件类型
        this.extendEventType([
            {
                type: "redo",
                ctrl: true,
                shift: true,
                code: 90
            },
            {
                type: "undo",
                ctrl: true,
                code: 90
            },
            {
                type: "copy",
                ctrl: true,
                code: 67
            },
            {
                type: "paste",
                ctrl: true,
                code: 86
            },
            {
                type: "delete",
                code: 46
            },
            {
                type: "right",
                code: 39
            },
            {
                type: "down",
                code: 40
            },
            {
                type: "left",
                code: 37
            },
            {
                type: "up",
                code: 38
            }
        ]);

        return null;
    };

    /**
     * @private _keyDownHandler 自定义键盘事件分发
     * */
    KeyboardListener.prototype._keyDownHandler = function _keyDownHandler(e){
        var strCommand = this._keyCodeProcess(e);

        var objEvent = {
            type: "",
            originEvent: e.originEvent
        };

        strCommand && this.callback["on" + strCommand](objEvent);

        return null;
    };

    /**
     * @private _keyCodeProcess 处理按键码
     * */
    KeyboardListener.prototype._keyCodeProcess = function _keyCodeProcess(e){
        var code = e.keyCode + "";
        var altKey = e.altKey;
        var ctrlKey = e.ctrlKey;
        var shiftKey = e.shiftKey;

        var threeKey = altKey && ctrlKey && shiftKey;
        var ctrlAlt = altKey && ctrlKey;
        var altShift = altKey && shiftKey;
        var ctrlShift = shiftKey && ctrlKey;

        var keyTypeSet = this.keyTypeSet;
        var resStr = "";

        if(threeKey){
            resStr = keyTypeSet.threeKey[code];
        } else if(ctrlAlt) {
            resStr = keyTypeSet.ctrlAlt[code];
        } else if(ctrlShift) {
            resStr = keyTypeSet.ctrlShift[code];
        } else if(altShift) {
            resStr = keyTypeSet.altShift[code];
        } else if(altKey) {
            resStr = keyTypeSet.altKey[code];
        } else if(ctrlKey) {
            resStr = keyTypeSet.ctrlKey[code];
        } else if(shiftKey) {
            resStr = keyTypeSet.shiftKey[code];
        } else {
            resStr = keyTypeSet.singleKey[code];
        }

        return resStr
    };

    /**
     * @private _setKeyComposition 自定义键盘事件
     * @param {Object} config 键盘事件配置方案
     * @param {String} config.type 自定义事件类型
     * @param {keyCode} config.code 按键的码值
     * @param {Boolean} [config.ctrl] 是否与 Ctrl 形成组合键
     * @param {Boolean} [config.alt] 是否与 Alt 形成组合键
     * @param {Boolean} [config.shift] 是否与 Shift 形成组合键
     * */
    KeyboardListener.prototype._setKeyComposition = function _setKeyComposition(config){
        var altKey = config.alt;
        var ctrlKey = config.ctrl;
        var shiftKey = config.shift;

        var threeKey = altKey && ctrlKey && shiftKey;
        var ctrlAlt = altKey && ctrlKey;
        var altShift = altKey && shiftKey;
        var ctrlShift = shiftKey && ctrlKey;
        var code = config.code + "";
        var type = config.type;

        if(threeKey){
            this.keyTypeSet.threeKey[code] = type;
        } else if(ctrlAlt) {
            this.keyTypeSet.ctrlAlt[code] = type;
        } else if(ctrlShift) {
            this.keyTypeSet.ctrlShift[code] = type;
        } else if(altShift) {
            this.keyTypeSet.altShift[code] = type;
        } else if(altKey) {
            this.keyTypeSet.altKey[code] = type;
        } else if(ctrlKey) {
            this.keyTypeSet.ctrlKey[code] = type;
        } else if(shiftKey) {
            this.keyTypeSet.shiftKey[code] = type;
        } else {
            this.keyTypeSet.singleKey[code] = type;
        }
        this.allEventType.push(type);
        this.callback["on" + type] = this._emptyEventHandler;
        // this.eventDiscibeSet = {};

        return null;
    };

    /**
     * @method extendEventType 扩展键盘事件类型
     * @param {Object|Array} config 键盘事件配置方案
     * @param {String} config.type 自定义事件类型
     * @param {keyCode} config.code 按键的码值
     * @param {Boolean} [config.ctrl] 是否与 Ctrl 形成组合键
     * @param {Boolean} [config.alt] 是否与 Alt 形成组合键
     * @param {Boolean} [config.shift] 是否与 Shift 形成组合键
     * */
    KeyboardListener.prototype.extendEventType = function extendEventType(config){
        var len = 0;
        if(config instanceof Array){
            len = config.length;
            while(len--){
                this._setKeyComposition(config[len]);
            }
        } else {
            this._setKeyComposition(config);
        }
        return this;
    };

    /**
     * @method bind 绑定自定义的键盘事件
     * @param {String} type 事件类型 如:["up", "down", "left", "right", "undo", "redo", "delete", zoomIn, "zoomOut"]
     * @param {Function} callback 回调函数,参数为一个自定义的仿事件对象
     * @param {String} description 对绑定事件的用途进行说明
     * */
    KeyboardListener.prototype.bind = function bind(type, callback, description){
        var allType = this.allEventType;
        if(allType.indexOf(type) === -1){
            throwError("不支持该事件类型,请先扩展该类型,或采用其他事件类型");
        }

        if(!(callback instanceof Function)){
            throwError("绑定的事件处理回调必须是函数类型");
        }

        this.callback["on" + type] = callback;

        this.eventDiscibeSet[type] = description || "没有该事件的描述";

        return this;
    };

    /**
     * @method unbind 解除事件绑定
     * @param {String} type 事件类型
     * */
    KeyboardListener.prototype.unbind = function unbind(type){
        this.callback["on" + type] = this._emptyEventHandler;

        return this;
    };
    
    return null;
}();           
               
                                           
                       
                 
            
                     
             
               

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103208.html

相关文章

  • javascript功能插件大集合 前端常用插件 js常用插件

    摘要:转载来源包管理器管理着库,并提供读取和打包它们的工具。能构建更好应用的客户端包管理器。一个整合和的最佳思想,使开发者能快速方便地组织和编写前端代码的下一代包管理器。很棒的组件集合。隐秘地使用和用户数据。 转载来源:https://github.com/jobbole/aw... 包管理器管理着 javascript 库,并提供读取和打包它们的工具。•npm – npm 是 javasc...

    netmou 评论0 收藏0
  • javascript功能插件大集合 前端常用插件 js常用插件

    摘要:转载来源包管理器管理着库,并提供读取和打包它们的工具。能构建更好应用的客户端包管理器。一个整合和的最佳思想,使开发者能快速方便地组织和编写前端代码的下一代包管理器。很棒的组件集合。隐秘地使用和用户数据。 转载来源:https://github.com/jobbole/aw... 包管理器管理着 javascript 库,并提供读取和打包它们的工具。•npm – npm 是 javasc...

    Hydrogen 评论0 收藏0
  • javascript功能插件大集合,写前端亲们记得收藏

    摘要:一个专注于浏览器端和兼容的包管理器。一个整合和的最佳思想,使开发者能快速方便地组织和编写前端代码的下一代包管理器。完全插件化的工具,能在中识别和记录模式。健壮的优雅且功能丰富的模板引擎。完整的经过充分测试和记录数据结构的库。 【导读】:GitHub 上有一个 Awesome – XXX 系列的资源整理。awesome-javascript 是 sorrycc 发起维护的 JS 资源列表...

    cfanr 评论0 收藏0
  • jQuery 事件(一) 鼠标与键盘事件

    jQuery 鼠标事件 click与dbclick事件 用交互操作中,最简单直接的操作就是点击操作。jQuery提供了两个方法一个是click方法用于监听用户单击操作,另一个方法是dbclick方法用于监听用户双击操作。这两个方法的用法是类似的,下面以click()事件为例 方法一:$ele.click() 绑定$ele元素,不带任何参数一般是用来指定触发一个事件,用的比较少 点击触发 $(ele...

    Gemini 评论0 收藏0
  • JavaScript 资源大全中文版

    摘要:官网全新的静态包管理器。官网一个整合和官网的最佳思想,使开发者能快速方便地组织和编写前端代码的下一代包管理器。官网小巧的兼容的所见即所得的富文本编辑器。官网富文本编辑器。官网由制作,适用于每天写作的富文本编辑器。 1. 包管理器 管理着 javascript 库,并提供读取和打包它们的工具。 npm:npm 是 javascript 的包管理器。官网 cnpm:cnpm 是 由于国...

    jzman 评论0 收藏0

发表评论

0条评论

付伦

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<