这篇讲ExtJs对事件的管理和调用
ExtJs对事件的管理非常强大,主要定义在Ext.EventManager对象(单例模式)中。先看注册事件监听方式,ExtJs提供了几种方式来注册元素监听函数
通过Ext.EventManager.addListener/on函数来为指定元素的某种事件注册监听函数。例如:Ext.EventManager.on('test1','click',clickMe,this);// clickMe是要执行的函数
通过ExtJs 元素的on函数来为自身注册某种事件的监听处理函数。例如: Ext.get('test1').on('click', clickMe,this,{preventDefault:true});
通过Ext.addBehaviors函数为多个元素的多个事件注册监听处理函数,该方法在ExtJs源码分析与学习—ExtJs核心代码扩展 中已介绍过。
具体来说可分为 标准配置方式 el.on(eventName,fn,scope,options); options参数是事件配置项,各项说明如下: scope : 可指定执行上下文 delegate :事件代理 stopEvent :阻止冒泡和默认行为 preventDefault :阻止默认行为 stopPropagation :停止事件冒泡 normalized : 仅传原生事件对象 delay :延迟执行 single : 仅执行一次 buffer :延迟执行,多次时最后一次覆盖前一次 target : 指定在父元素上执行
私有配置多事件注册 el.on({'click':{fn:this.onClick,scope:this,delay:100}, 'mouseover':{fn:this.onMouseOver,scope:this}, … });
共享配置多事件注册 el.on({'click':this. onClick, 'mouseover':this.onMouseOver, …, scope:this});
多元素多事件注册 Ext. addBehaviors({ '#foo a@click' : function(e, t){ // do something }, '#foo a, #bar span.some-class@mouseover' : function(){ // do something } ); 注意这种共享配置多事件注册监听函数只有两个参数,不能传递配置对象来进行配置,与前三种不同。
上述几种方式最终还是调用Ext.EventManager.addListener函数来注册监听的,该函数源代码如下:
addListener : function (element, eventName, fn, scope, options){
if ( typeof eventName == 'object' ){
var o = eventName, e, val;
for (e in o){
val = o[e];
if (!propRe.test(e)){
if (Ext.isFunction(val)){
listen(element, e, o, val, o.scope);
}else {
listen(element, e, val);
}
}
}
} else {
listen(element, eventName, options, fn, scope);
}
},
该监听函数统一调用了listen,源代码如下:
var listen = function (element, ename, opt, fn, scope){
var o = (!opt || typeof opt == "boolean" ) ? {} : opt;
fn = fn || o.fn; scope = scope || o.scope;
var el = Ext.getDom(element);
if (!el){
throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.' ;
}
var h = function (e){
if (!window[xname]){
return ;
}
e = Ext.EventObject.setEvent(e);
var t;
if (o.delegate){
t = e.getTarget(o.delegate, el);
if (!t){
return ;
}
}else {
t = e.target;
}
if (o.stopEvent === true ){
e.stopEvent();
}
if (o.preventDefault === true ){
e.preventDefault();
}
if (o.stopPropagation === true ){
e.stopPropagation();
}
if (o.normalized === false ){
e = e.browserEvent;
}
fn.call(scope || el, e, t, o);
};
if (o.target){
h = createTargeted(h, o);
}
if (o.delay){
h = createDelayed(h, o);
}
if (o.single){
h = createSingle(h, el, ename, fn, scope);
}
if (o.buffer){
h = createBuffered(h, o);
}
addListener(el, ename, fn, h, scope);
return h;
};
在listen函数最后调用了私有函数addListener去注册事件监听函数
function addListener(el, ename, fn, task, wrap, scope){
el = Ext.getDom(el);
var id = getId(el),
es = Ext.elCache[id].events,
wfn;
wfn = E.on(el, ename, wrap);
es[ename] = es[ename] || [];
es[ename].push([fn, wrap, scope, wfn, task]);
if (el.addEventListener && ename == "mousewheel" ){
var args = [ "DOMMouseScroll" , wrap, false ];
el.addEventListener.apply(el, args);
Ext.EventManager.addListener(WINDOW, 'unload' , function (){
el.removeEventListener.apply(el, args);
});
}
if (el == DOC && ename == "mousedown" ){
Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
}
}
以上代码讲解了addListener函数,主要是对原生事件的包装、注册、配置项的处理和事件列表(Ext.elCache[id].events,该列表的初始化定义在Ext.Element.addToCache(new Ext.Element(el), id)方法中,后续会分析)的维护,详细说明请参照代码中的注释。
接下来看删除事件
removeListener : function (el, eventName, fn, scope){
el = Ext.getDom(el);
var id = getId(el),
f = el && (Ext.elCache[id].events)[eventName] || [],
wrap, i, l, k, len, fnc;
for (i = 0, len = f.length; i < len; i++) {
if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
if (fnc[4]) {
fnc[4].cancel();
}
k = fn.tasks && fn.tasks.length;
if (k) {
while (k--) {
fn.tasks[k].cancel();
}
delete fn.tasks;
}
wrap = fnc[1];
E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
if (wrap && el.addEventListener && eventName == "mousewheel" ){
el.removeEventListener("DOMMouseScroll" , wrap, false );
}
if (wrap && el == DOC && eventName == "mousedown" ){
Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
}
f.splice(i, 1);
if (f.length === 0) {
delete Ext.elCache[id].events[eventName];
}
for (k in Ext.elCache[id].events) {
return false ;
}
Ext.elCache[id].events = {};
return false ;
}
}
},
代码中方法 removeAll 的功能为移除某个元素所有的事件,而方法 getListeners 的功能为返回元素el对应的事件名eventName所有的监听函数,请看下面的例子
"button" value= "点击我" id= "btn" >
var buttonEl = Ext.get( 'btn' );
buttonEl.on('click' , function (e,t,o){alert(e.type + '1' );}, this );
buttonEl.on('click' , function (e,t,o){alert(e.type + '2' );}, this );
buttonEl.on('click' , function (e,t,o){alert(e.type + '3' );}, this ); var
listeners = Ext.EventManager.getListeners(buttonEl,'click' );
alert(listeners);
再看方法purgeElement : function(el, recurse, eventName)实现的功能是递归删除元素el以及el的子元素上已注册的事件 参数 el 要删除事件对应的元素 参数 recurse 是否删除el子元素上的事件,true为是 参数 eventName 已注册的事件名,如果没有传入eventName参数,默认则把删除所有的注册的事件
下面看该类中方法onDocumentReady,该方法的简写是Ext.onReady,该方法的功能是当Document准备好的时候触发传入的业务函数。源代码如下
onDocumentReady : function (fn, scope, options){
if (Ext.isReady) {
docReadyEvent || (docReadyEvent = new Ext.util.Event());
docReadyEvent.addListener(fn, scope, options);
docReadyEvent.fire();
docReadyEvent.listeners = [];
} else {
if (!docReadyEvent) {
initDocReady();
}
options = options || {};
options.delay = options.delay || 1;
docReadyEvent.addListener(fn, scope, options);
}
},
该方法中当DOM文档没有完全载入时,就通过initDocReady等待,并且把参数中的业务函数注册到docReadyEvent对象中,好让在initDocReady函数注册的fireDocReady函数执行。initDocReady的代码如下
function initDocReady(){
docReadyEvent || (docReadyEvent = new Ext.util.Event());
if (DETECT_NATIVE) {
DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false );
}
if (Ext.isIE){
if (!checkReadyState()){
checkReadyState.bindIE = true ;
DOC.attachEvent('onreadystatechange' , checkReadyState);
}
}else if (Ext.isOpera ){
(DOC.readyState == COMPLETE && checkStyleSheets()) ||
DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false );
}else if (Ext.isWebKit){
checkReadyState();
}
E.on(WINDOW, "load" , fireDocReady);
}
接下来是两个自运行函数(闭包函数的处理) initExtCss 初始化Ext Css,用来区分是何种浏览器或操作系统,加载到body上 supportTests 支持测试,怎么用不太明白
以上是对Ext.EventManager代码中主要方法和功能的分析,至此完成了ExtJs对浏览器事件的封装与调用,接下来讲解ExtJs自定义事件的处理,即ExtJs组件事件。