您好,登錄后才能下訂單哦!
這篇文章主要介紹了 jQuery中鍵盤事件監(jiān)聽控件的案例分析,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
最近項(xiàng)目里要做一個(gè)畫板,需要對(duì)鍵盤事件進(jìn)行監(jiān)聽,來進(jìn)行諸如撤回、重做、移動(dòng)、縮放等操作,因此順手實(shí)現(xiàn)了一個(gè)鍵盤事件監(jiān)聽控件,期間略有收獲,整理出來,希望對(duì)大家有所幫助,更希望能獲得高手的指點(diǎn)。
似乎瀏覽器的鍵盤事件只能被那些可以獲得焦點(diǎn)的元素設(shè)置監(jiān)聽,而通常需要監(jiān)聽事件的 <p>、<CANVAS> 元素都不能獲得焦點(diǎn),因此需要修改目標(biāo)元素的某些屬性使其可以獲得焦點(diǎn),另外一種可行的方法是將事件委托給諸如 <INPUT> 標(biāo)簽。這里采用的是第一類方法,當(dāng)然,可以修改的屬性也不止一種,例如,對(duì)于 <p> 標(biāo)簽可以將其 “editable” 屬性設(shè)為 true,而這里采用的是給其設(shè)一個(gè) tabindex 值。代碼如下:
$ele.attr('tabindex', 1);
另外,焦點(diǎn)事件的觸發(fā)需要點(diǎn)擊元素或者 TAB 切換,而這并不符合人類的直覺,因此需要監(jiān)聽鼠標(biāo)移入事件,使目標(biāo)元素“自動(dòng)”地獲得焦點(diǎn):
$ele.on('mouseenter', function(){ $ele.focus(); });
由于項(xiàng)目面向的客戶所使用的瀏覽器以chrome為主(實(shí)際上是36x瀏覽器),因此沒有針對(duì)瀏覽器做任何適配,僅僅使用了 jQuery的事件監(jiān)聽:
$ele.on('keydown', this._keyDownHandler.bind(this));
由于實(shí)現(xiàn)是控件化的,所以定義了一個(gè)私有方法 _keyDownHandler 來響應(yīng)鍵盤的動(dòng)作。
jQuery事件監(jiān)聽器返回的事件對(duì)象信息較多,因此需要進(jìn)行甄別,為此定義了一個(gè)私有方法 _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 是一個(gè)類似于查找表的對(duì)象,里面存儲(chǔ)了 ctrl、shift、alt按鈕的各種類型組合,每種組合下又分別按照按鍵碼存儲(chǔ)一個(gè)自定義事件類型字符串,事件發(fā)生之后會(huì)從這里返回這個(gè)字符串,當(dāng)然,沒有對(duì)應(yīng)自定義事件的時(shí)候,就老老實(shí)實(shí)地返回空字符串。
_keyCodeProcess 方法從事件中提取出了事件類型,我們提前將監(jiān)聽的回調(diào)函數(shù)存儲(chǔ)在一個(gè)查找表 callback 中,并且“巧妙”地使得其鍵名剛好為自定義事件字符串前面加個(gè)“on”前綴,就可以方便地調(diào)用了,前述 _keyDownHandler 正是為此而設(shè)計(jì)的:
function _keyDownHandler(e){ var strCommand = this._keyCodeProcess(e); var objEvent = { type: '', originEvent: e.originEvent }; strCommand && this.callback['on' + strCommand](objEvent); return null; };
前面說了,我們是把回調(diào)函數(shù)存儲(chǔ)起來適時(shí)調(diào)用的,因此需要對(duì)外暴露一個(gè)“訂閱”接口,讓開發(fā)者可以方便地把自己的回調(diào)函數(shù)存儲(chǔ)到對(duì)象實(shí)例中去,為此,我定義了一個(gè) .bind接口:
function bind(type, callback, description){ var allType = this.allEventType; if(allType.indexOf(type) === -1){ throwError('不支持改事件類型,請(qǐng)先擴(kuò)展該類型,或采用其他事件類型'); } if(!(callback instanceof Function)){ throwError('綁定的事件處理回調(diào)必須是函數(shù)類型'); } this.callback['on' + type] = callback; this.eventDiscibeSet[type] = description || '沒有該事件的描述'; return this; };
由于是給人用的,所以順帶做了下類型檢查。
根據(jù)接口的“對(duì)稱性”,有訂閱最好也有解除訂閱,因此定義了 .unbind接口,只有一句代碼,實(shí)現(xiàn)如下:
function unbind(type){ this.callback['on' + type] = this._emptyEventHandler; return this; };
鍵盤事件的組合豐富多彩,如果全部?jī)?nèi)置在控件中的話,會(huì)是很臃腫的,因此除了少數(shù)幾個(gè)常見的組合鍵之外,開發(fā)者可以通過 .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 是一個(gè)私有方法,用來寫入自定義鍵盤事件的方法:
_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; };
這樣,一個(gè)鍵盤事件監(jiān)聽控件就大功告成了,下面是完整實(shí)現(xiàn)代碼:
/** * @constructor 鍵盤事件監(jiān)聽器 * */ function KeyboardListener(param){ this._init(param); } !function(){ /** * @private {String} param.ele 事件對(duì)象選擇器 * */ KeyboardListener.prototype._init = function _init(param){ this.$ele = $(param.ele); this._initEvents(); this._initEventType(); return null; }; /** * @private _emptyEventHandler 空白事件響應(yīng) * */ 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: {} }; // 支持一些內(nèi)建的鍵盤事件類型 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 自定義鍵盤事件分發(fā) * */ 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 擴(kuò)展鍵盤事件類型 * @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 回調(diào)函數(shù),參數(shù)為一個(gè)自定義的仿事件對(duì)象 * @param {String} description 對(duì)綁定事件的用途進(jìn)行說明 * */ KeyboardListener.prototype.bind = function bind(type, callback, description){ var allType = this.allEventType; if(allType.indexOf(type) === -1){ throwError('不支持改事件類型,請(qǐng)先擴(kuò)展該類型,或采用其他事件類型'); } if(!(callback instanceof Function)){ throwError('綁定的事件處理回調(diào)必須是函數(shù)類型'); } 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; }; }();
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享 jQuery中鍵盤事件監(jiān)聽控件的案例分析內(nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,遇到問題就找億速云,詳細(xì)的解決方法等著你來學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。