基于 jQuery 实现键盘事件监听控件

作者:madRain 时间:2024-06-17 11:37:09 

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

1. 自动获取焦点

似乎浏览器的键盘事件只能被那些可以获得焦点的元素设置监听,而通常需要监听事件的 <DIV>、<CANVAS> 元素都不能获得焦点,因此需要修改目标元素的某些属性使其可以获得焦点,另外一种可行的方法是将事件委托给诸如 <INPUT> 标签。这里采用的是第一类方法,当然,可以修改的属性也不止一种,例如,对于 <DIV> 标签可以将其 “editable” 属性设为 true,而这里采用的是给其设一个 tabindex 值。代码如下:

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

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


$ele.on('mouseenter', function(){
 $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 是一个私有方法,用来写入自定义键盘事件的方法:


_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;
   }
   return null;
 };

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


/**
* @constructor 键盘事件 *
* */
function KeyboardListener(param){
 this._init(param);
}
!function(){
 /**
  * @private {String} param.ele 事件对象选择器
  * */
 KeyboardListener.prototype._init = function _init(param){
   this.$ele = $(param.ele);
   this._initEvents();
   this._initEventType();
   return null;
 };
 /**
  * @private _emptyEventHandler 空白事件响应
  * */
 KeyboardListener.prototype._emptyEventHandler = function _emptyEventHandler(){
   return null;
 };
 /**
  * @private _initEventType 初始化所有初始自定义事件类型
  * */
 KeyboardListener.prototype._initEventType = function _initEventType(){
   var allType = ['up', 'down', 'left', 'right', 'undo', 'redo', 'zoomIn', 'zoomOut', 'delete'];
   var intLen = allType.length;
   this.allEventType = allType;
   this.callback = {};
   this.eventDiscibeSet = {};
   for(var intCnt = 0; intCnt < intLen; intCnt++){
     this.callback['on' + allType[intCnt]] = KeyboardListener.prototype._emptyEventHandler;
   }
   return null;
 };
 /**
  * @private _initEvents 绑定 DOM 事件
  * */
 KeyboardListener.prototype._initEvents = function _initEvents(){
   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 + '';
   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;
   }
   return null;
 };
 /**
  * @method extendEventType 扩展键盘事件类型
  * @param {Object|Array<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.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;
 };
}();

总结

以上所述是小编给大家介绍的基于 jQuery 实现键盘事件监听控件,希望对大家有所帮助

来源:https://segmentfault.com/a/1190000018759478?utm_source=tuicool&utm_medium=referral

标签:jquery,键盘事件,监听控件
0
投稿

猜你喜欢

  • 利用Python实现智能合约的示例详解

    2022-04-06 00:45:23
  • 简单了解django索引的相关知识

    2021-10-01 14:55:43
  • Python标准库uuid模块(生成唯一标识)详解

    2023-07-04 14:03:05
  • SQL Server视图管理中的四个限制条件

    2009-03-06 14:24:00
  • django 邮件发送模块smtp使用详解

    2021-09-24 23:04:15
  • Python实现类别变量的独热编码

    2023-10-17 02:37:33
  • 详解Python牛顿插值法

    2023-03-05 05:58:27
  • python实现银行管理系统

    2021-07-09 00:33:49
  • 如何让新安装的MySQL数据库变得更安全

    2009-01-04 13:19:00
  • python如何调用现有的matlab函数

    2023-04-28 22:07:09
  • asp如何做一个全面的服务器探测器?

    2010-07-12 18:55:00
  • Keras—embedding嵌入层的用法详解

    2021-06-05 01:08:17
  • Python中Matplotlib的点、线形状、颜色以及绘制散点图

    2023-06-11 00:32:26
  • MySQL数据库数据删除操作详解

    2024-01-13 11:55:42
  • python实现大文本文件分割成多个小文件

    2022-02-18 12:36:06
  • 详解在Python中以绝对路径或者相对路径导入文件的方法

    2021-10-09 19:37:24
  • JS实现网页滚动条感应鼠标变色的方法

    2024-04-18 10:52:44
  • 基于Python利用Pygame实现翻转图像

    2021-05-20 00:05:08
  • SQL Server自动生成日期加数字的序列号

    2024-01-12 21:29:42
  • Django的分页器实例(paginator)

    2023-06-23 03:22:35
  • asp之家 网络编程 m.aspxhome.com