最近一直忙着做产品,所以好久没有写文章了,下面接着把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); //代理按键的监听处理函数,对配置对象中的fn/handle函数进行代理加工处理 var handler = function(e){ //如果指定'shift', 'ctrl', 'alt',而事件对象没有按下指定的'shift', 'ctrl', 'alt',那么就不进行处理 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() { //cut }, scope : this },{//复制 key : 'c', ctrl : true, fn : function() { //copy }, scope : this },{//粘贴 key : 'v', ctrl : true, fn : function() { //paste }, 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) { // call stopKeyUp() on "keyup" event 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( /** * @event mousedown * 当鼠标按下的时候触发。 * Fires when the mouse button is depressed. * @param {Ext.util.ClickRepeater} this * @param {Ext.EventObject} e */ "mousedown", /** * @event click * 当元素被按下接受到按下的消息的时候出发,比mousedown事件慢。 * Fires on a specified interval during the time the element is pressed. * @param {Ext.util.ClickRepeater} this * @param {Ext.EventObject} e */ "click", /** * @event mouseup * 当鼠标释放后触发。 * Fires when the mouse key is released. * @param {Ext.util.ClickRepeater} this * @param {Ext.EventObject} e */ "mouseup" ); if(!this.disabled){ this.disabled = true; this.enable(); } // allow inline handler 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); // Do not honor delay or interval if acceleration wanted. 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会越来越快。