基於 jQuery 實現鍵盤事件監聽控件


原文鏈接:https://www.jb51.net/article/159116.htm

最近項目里要做一個畫板,需要對鍵盤事件進行監聽,來進行諸如撤回、重做、移動、縮放等操作,因此順手實現了一個鍵盤事件監聽控件,期間略有收獲,整理出來,希望對大家有所幫助,更希望能獲得高手的指點。

1. 自動獲取焦點

似乎瀏覽器的鍵盤事件只能被那些可以獲得焦點的元素設置監聽,而通常需要監聽事件的 <DIV>、<CANVAS> 元素都不能獲得焦點,因此需要修改目標元素的某些屬性使其可以獲得焦點,另外一種可行的方法是將事件委托給諸如 <INPUT> 標簽。這里采用的是第一類方法,當然,可以修改的屬性也不止一種,例如,對於 <DIV> 標簽可以將其 “editable” 屬性設為 true,而這里采用的是給其設一個 tabindex 值。代碼如下:

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

另外,焦點事件的觸發需要點擊元素或者 TAB 切換,而這並不符合人類的直覺,因此需要監聽鼠標移入事件,使目標元素“自動”地獲得焦點:

?
1
2
3
$ele.on( 'mouseenter' , function (){
   $ele.focus();
});

2. 監聽鍵盤事件

由於項目面向的客戶所使用的瀏覽器以chrome為主(實際上是36x瀏覽器),因此沒有針對瀏覽器做任何適配,僅僅使用了 jQuery的事件監聽:

?
1
$ele.on( 'keydown' , this ._keyDownHandler.bind( this ));

由於實現是控件化的,所以定義了一個私有方法 _keyDownHandler 來響應鍵盤的動作。

3. 按鍵事件甄別

jQuery事件監聽器返回的事件對象信息較多,因此需要進行甄別,為此定義了一個私有方法 _keyCodeProcess 來處理按鍵

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 正是為此而設計的:

?
1
2
3
4
5
6
7
8
9
function _keyDownHandler(e){
     var strCommand = this ._keyCodeProcess(e);
     var objEvent = {
       type: '' ,
       originEvent: e.originEvent
     };
     strCommand && this .callback[ 'on' + strCommand](objEvent);
     return null ;
   };

5. 事件訂閱與解除訂閱

前面說了,我們是把回調函數存儲起來適時調用的,因此需要對外暴露一個“訂閱”接口,讓開發者可以方便地把自己的回調函數存儲到對象實例中去,為此,我定義了一個 .bind接口:

?
1
2
3
4
5
6
7
8
9
10
11
12
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接口,只有一句代碼,實現如下:

?
1
2
3
4
function unbind(type){
     this .callback[ 'on' + type] = this ._emptyEventHandler;
     return this ;
   };

6.擴展自定義事件類型

鍵盤事件的組合豐富多彩,如果全部內置在控件中的話,會是很臃腫的,因此除了少數幾個常見的組合鍵之外,開發者可以通過 .extendEventType 方法,來自定義組合鍵和返回的字符串:

?
1
2
3
4
5
6
7
8
9
10
11
12
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 是一個私有方法,用來寫入自定義鍵盤事件的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
_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 ;
   };

這樣,一個鍵盤事件監聽控件就大功告成了,下面是完整實現代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/**
  * @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 實現鍵盤事件監聽控件,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM