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

近来项目里要做一个画板,须要对键盘事宜举行监听,来举行诸如撤回、重做、挪动、缩放等快捷键操纵,因而随手完成了一个键盘事宜监听控件,略有收成,整理出来,愿望对人人有所协助,更愿望能取得高手的指导。

1. 自动猎取核心

好像浏览器的键盘事宜只能被那些能够取得核心的元素设置监听,而一般须要监听事宜的 <DIV>、<CANVAS> 元素都不能取得核心,因而须要修正目的元素的某些属性使其能够取得核心,别的一种可行的要领是将事宜托付给诸如 <INPUT> 标签。这里采纳的是第一类要领,固然,能够修正的属性也不止一种,比方,关于 <DIV> 标签能够将其 “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

;
} else if(ctrlAlt) {
resStr = keyTypeSet.ctrlAlt

;
} else if(ctrlShift) {
resStr = keyTypeSet.ctrlShift

;
} else if(altShift) {
resStr = keyTypeSet.altShift

;
} else if(altKey) {
resStr = keyTypeSet.altKey

;
} else if(ctrlKey) {
resStr = keyTypeSet.ctrlKey

;
} else if(shiftKey) {
resStr = keyTypeSet.shiftKey

;
} else {
resStr = keyTypeSet.singleKey

;
}

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

= config.type;
} else if(ctrlAlt) {
this.keyTypeSet.ctrlAlt

= config.type;
} else if(ctrlShift) {
this.keyTypeSet.ctrlShift

= config.type;
} else if(altShift) {
this.keyTypeSet.altShift

= config.type;
} else if(altKey) {
this.keyTypeSet.altKey

= config.type;
} else if(ctrlKey) {
this.keyTypeSet.ctrlKey

= config.type;
} else if(shiftKey) {
this.keyTypeSet.shiftKey

= config.type;
} else {
this.keyTypeSet.singleKey

= 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

;
} else if(ctrlAlt) {
resStr = keyTypeSet.ctrlAlt

;
} else if(ctrlShift) {
resStr = keyTypeSet.ctrlShift

;
} else if(altShift) {
resStr = keyTypeSet.altShift

;
} else if(altKey) {
resStr = keyTypeSet.altKey

;
} else if(ctrlKey) {
resStr = keyTypeSet.ctrlKey

;
} else if(shiftKey) {
resStr = keyTypeSet.shiftKey

;
} else {
resStr = keyTypeSet.singleKey

;
}

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

= type;
} else if(ctrlAlt) {
this.keyTypeSet.ctrlAlt

= type;
} else if(ctrlShift) {
this.keyTypeSet.ctrlShift

= type;
} else if(altShift) {
this.keyTypeSet.altShift

= type;
} else if(altKey) {
this.keyTypeSet.altKey

= type;
} else if(ctrlKey) {
this.keyTypeSet.ctrlKey

= type;
} else if(shiftKey) {
this.keyTypeSet.shiftKey

= type;
} else {
this.keyTypeSet.singleKey

= type;
}
this.allEventType.push(type);
this.callback['on' + type] = this._emptyEventHandler;
// this.eventDiscibeSet = {};

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;
};

return null;
}();

    原文作者:madRain
    原文地址: https://segmentfault.com/a/1190000018759478
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞