最近一直忙着做产品,所以好久没有写文章了,下面接着把ExtJs事件机制最后一点内容写完。主要是介绍Ext提供的三个辅助实现事件类——快捷键、导航键和鼠标按键事件。
快捷键 Ext.KeyMap
该功能的实现被封装在类Ext.KeyMap中
- Ext.KeyMap = function(el, config, eventName){
- this.el = Ext.get(el);
- this.eventName = eventName || "keydown";
- this.bindings = [];
- if(config){
- this.addBinding(config);
- }
- this.enable();
- };
该类实现分为三步,首先找到注册快捷键的元素el,然后把config参数转换为事件的监听函数this.addBinding(config),最后再注册该监听函数this.enable()。先看addBinding方法的实现
- addBinding : function(config){
- if(Ext.isArray(config)){
- Ext.each(config, function(c){
- this.addBinding(c);
- }, this);
- return;
- }
- var keyCode = config.key,
- fn = config.fn || config.handler,
- scope = config.scope;
-
- if (config.stopEvent) {
- this.stopEvent = config.stopEvent;
- }
-
- if(typeof keyCode == "string"){
- var ks = [];
- var keyString = keyCode.toUpperCase();
- for(var j = 0, len = keyString.length; j < len; j++){
- ks.push(keyString.charCodeAt(j));
- }
- keyCode = ks;
- }
- var keyArray = Ext.isArray(keyCode);
-
-
- var handler = function(e){
-
- if(this.checkModifiers(config, e)){
- var k = e.getKey();
- if(keyArray){
- for(var i = 0, len = keyCode.length; i < len; i++){
- if(keyCode[i] == k){
- if(this.stopEvent){
- e.stopEvent();
- }
- fn.call(scope || window, k, e);
- return;
- }
- }
- }else{
- if(k == keyCode){
- if(this.stopEvent){
- e.stopEvent();
- }
- fn.call(scope || window, k, e);
- }
- }
- }
- };
- this.bindings.push(handler);
- },
config配置项支持以下属性
属性 类型 描述
---------- --------------- ----------------------------------------------------------------------
key String/Array 进行处理的单个keycode或keycodes组成的数组
shift Boolean True:只有shift按下的的同时处理key (默认false)
ctrl Boolean True:只有ctrl按下的的同时处理key (默认false)
handler Function 当KeyMap找到预期的组合键时所执行的函数
alt Boolean True:只有alt按下的的同时处理key (默认false)
fn Function 当组合键按下后回调函数
scope Object 回调函数的作用域
stopEvent Boolean 用来停止事件冒泡,阻止元素默认行为。
再看enable方法,把事件注册到元素上
- enable: function(){
- if(!this.enabled){
- this.el.on(this.eventName, this.handleKeyDown, this);
- this.enabled = true;
- }
- },
为指定的el元素注册事件
例子:常用复制、剪切、粘贴的实现
- var config = [{
- key : 'x',
- ctrl : true,
- fn : function() {
-
- },
- scope : this
- },{
- key : 'c',
- ctrl : true,
- fn : function() {
-
- },
- scope : this
- },{
- key : 'v',
- ctrl : true,
- fn : function() {
-
- },
- scope : this
- },{
- key : 'abcdefghigklmnopqrstuvwxyz0123456789',
- ctrl : false,
- shift : false,
- alt : false,
- fn : function(k, e) {
- alert(k);
- },
- scope : this
- }];
-
- var map = new Ext.KeyMap("my-element", config);
导航键 Ext.KeyNav
导航键的实现被封装在类Ext.KeyNav中,首先看其构造函数
- Ext.KeyNav = function(el, config){
- this.el = Ext.get(el);
- Ext.apply(this, config);
- if(!this.disabled){
- this.disabled = true;
- this.enable();
- }
- };
再看注册监听函数
- enable: function() {
- if (this.disabled) {
- if (Ext.isSafari2) {
-
- this.el.on('keyup', this.stopKeyUp, this);
- }
-
- this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
- this.disabled = false;
- }
- },
IE及其他一些浏览器的keyPress事件不会对非字母数字键进行冒泡,所以采用keyDown事件替代keyPress事件。而该事件的处理函数relay起了代理作用
- relay : function(e){
- var k = e.getKey(),
- h = this.keyToHandler[k];
- if(h && this[h]){
- if(this.doRelay(e, this[h], h) !== true){
- e[this.defaultEventAction]();
- }
- }
- },
KeyToHandler是键名和键值对应的hash表,通过event对象获得了键值之后,再和Ext.KeyNav类中12个导航键名比较,看看其是否为导航键,如果是,再看看有没有注册的处理函数,有才运行该监听函数。
该导航键共提供了12个,分别为
keyToHandler : {
37 : "left",
39 : "right",
38 : "up",
40 : "down",
33 : "pageUp",
34 : "pageDown",
46 : "del",
36 : "home",
35 : "end",
13 : "enter",
27 : "esc",
9 : "tab"
},
例子
- var nav = new Ext.KeyNav("my-element", {
- "left" : function(e){
- this.moveLeft(e.ctrlKey);
- },
- "right" : function(e){
- this.moveRight(e.ctrlKey);
- },
- "enter" : function(e){
- this.save();
- },
- scope : this
- });
鼠标按住事件 Ext.util.ClickRepeater
鼠标按住事件就是用鼠标按住某个元素,会根据指定的时间间隔来反复地执行同样的动作。该功能的实现被封装在类Ext.util.ClickRepeater,首先看构造函数
- constructor : function(el, config){
- this.el = Ext.get(el);
- this.el.unselectable();
-
- Ext.apply(this, config);
-
- this.addEvents(
-
-
-
-
-
-
-
- "mousedown",
-
-
-
-
-
-
-
- "click",
-
-
-
-
-
-
-
- "mouseup"
- );
-
- if(!this.disabled){
- this.disabled = true;
- this.enable();
- }
-
-
- if(this.handler){
- this.on("click", this.handler, this.scope || this);
- }
-
- Ext.util.ClickRepeater.superclass.constructor.call(this);
- },
该构造函数中调用了this.enable();来处理鼠标按下事件,然后如果配置项声明了handler处理函数,会把该函数注册到cilck事件中。下面看方法enable
- enable: function(){
- if(this.disabled){
- this.el.on('mousedown', this.handleMouseDown, this);
- if (Ext.isIE){
- this.el.on('dblclick', this.handleDblClick, this);
- }
- if(this.preventDefault || this.stopDefault){
- this.el.on('click', this.eventOptions, this);
- }
- }
- this.disabled = false;
- },
该方法中给元素el注册了mousedown事件,如果是IE浏览器,还注册了dblclick事件,然后根据配置项来阻止默认或冒泡处理,下面看handleMouseDown
- handleMouseDown : function(e){
- clearTimeout(this.timer);
- this.el.blur();
- if(this.pressClass){
- this.el.addClass(this.pressClass);
- }
- this.mousedownTime = new Date();
-
- Ext.getDoc().on("mouseup", this.handleMouseUp, this);
- this.el.on("mouseout", this.handleMouseOut, this);
-
- this.fireEvent("mousedown", this, e);
- this.fireEvent("click", this, e);
-
-
- if (this.accelerate) {
- this.delay = 400;
- }
- this.timer = this.click.defer(this.delay || this.interval, this, [e]);
- },
当用户在某个元素上按下鼠标时,首先让该元素失去焦点,改变样式,同时还分别为鼠标按键松开或移出注册了监听函数,最后按指定的间隔来推迟运行click函数
- click : function(e){
- this.fireEvent("click", this, e);
- this.timer = this.click.defer(this.accelerate ?
- this.easeOutExpo(this.mousedownTime.getElapsed(),
- 400,
- -390,
- 12000) :
- this.interval, this, [e]);
- },
该函数递归调用click函数,如果设置了this.accelerate,时间间隔就会按一定的算法越运行越短,即运行click会越来越快。