jQuery为我们提供了一个非常丰富好用的事件API,相对于浏览器自身的事件接口,jQuery有以下特点:
1. 对浏览器进行了兼容性处理,用户使用不需要考虑浏览器兼容性问题
2. 事件数据是保持在内部缓存中的,而不是保持在DOM节点上
3. 事件委托机制,提供了一个非常简单的事件委托使用方法
4. 自定义事件,不仅仅是浏览器事件,可以创建自定义事件
5. 辅助功能,比如命名空间,事件数据等等
那么下面就来看看jQuery是怎么实现的,首先扫一眼Event模块的源码结构:
总共900行,总共包括5部分:
1. 正则和辅助函数:最上面的正则表达式和3个辅助函数,后面会说到
2. event辅助类:用于事件处理的辅助对象
3. Event可写事件对象,等同于浏览器事件中的event对象,但Event对象的数据是可写的,添加了jQuery的一些属性。
4. 兼容性处理:事件的兼容性处理逻辑
5. 对外API,添加到jquery实例对象的对外API
这段代码结构逻辑上分为4层,
第一层是对外API,这段是对用户调用的参数处理
第二层是event辅助类,用户调用以后会调用辅助类的各种方法
第三层是Event对象,event处理过程中会创建Event对象来代替浏览器事件中的event对象
第四层是兼容性处理,针对浏览器中有些事件的兼容性问题,进行了处理
1. 我们从对外API开始说起,比如:
console.log(1);
});
我们对$("#div_main")这个jquery对象调用了on方法,就可以注册一个点击事件,on方法是什么?见对外API那部分代码
on: function(types, selector, data, fn, /*INTERNAL*/one) {varorigFn, type;//Types can be a map of types/handlers
if (typeof types === "object") {//( types-Object, selector, data )
if (typeof selector !== "string") {//( types-Object, data )
data = data ||selector;
selector=undefined;
}for (type intypes) {this.on(type, selector, data, types[type], one);
}return this;
}if (data == null && fn == null) {//( types, fn )
fn =selector;
data= selector =undefined;
}else if (fn == null) {if (typeof selector === "string") {//( types, selector, fn )
fn =data;
data=undefined;
}else{//( types, data, fn )
fn =data;
data=selector;
selector=undefined;
}
}if (fn === false) {
fn=returnFalse;
}else if (!fn) {return this;
}if (one === 1) {
origFn=fn;
fn= function(event) {//Can use an empty set, since event contains the info
jQuery().off(event);return origFn.apply(this, arguments);
};//Use same guid so caller can remove using origFn
fn.guid = origFn.guid || (origFn.guid = jQuery.guid++);
}return this.each(function() {
jQuery.event.add(this, types, fn, data, selector);
});
},
on
这段代码其实是对用户调用的方法进行了各种参数情况逻辑判断,最后归根到jQuery.event.add方法,也就是最后是调用了event辅助类的方法,首先on方法对用户传入的参数进行了判断,主要可能有以下几种情况:
(1) 以json方式传入多个事件方法,比如:
on({"click":fn1,"blur":fn2},"li",data);
on({"click":fn1,"blur":fn2},data);
//json对象格式
if (typeof types === "object") {
//selector不是字符串是数据,则重新设置数据变量,on({"click":fn1,"blur":fn2},data) if (typeof selector !== "string") { data = data ||selector;
selector=undefined;
}
//对每个json属性递归调用on方法for (type intypes) {this.on(type, selector, data, types[type], one);
}return this;
}
(2)其他三种情况:on("click",fn) on("click","li",fn) on("click",data,fn)
if (data == null && fn == null) {//类似on("click",fn1),重置变量
fn =selector;
data= selector =undefined;
}else if (fn == null) {if (typeof selector === "string") {//类似on("click","li",fn)
fn =data;
data=undefined;
}else{//类似on("click",data,fn);
fn =data;
data=selector;
selector=undefined;
}
}
//快捷方式,如果fn参数传入false,自动设置为false方法if (fn === false) {
fn=returnFalse;
}else if (!fn) {return this;
}
if (one === 1) {//只执行一次的方法,执行一次后删除本事件对象
origFn=fn;
fn= function(event) {//Can use an empty set, since event contains the info
jQuery().off(event);return origFn.apply(this, arguments);
};//Use same guid so caller can remove using origFn
fn.guid = origFn.guid || (origFn.guid = jQuery.guid++);
}return this.each(function() {//对每个jquery实例进行调用
jQuery.event.add(this, types, fn, data, selector);
});
然后是one方法,其实就是调用上面的on方法,带上one参数
one: function(types, selector, data, fn) {return this.on(types, selector, data, fn, 1);
},
off方法,和on方法类似,针对输入参数的几种情况最终是调用了event辅助类的remove方法。
off: function(types, selector, fn) {varhandleObj, type;if (types && types.preventDefault &&types.handleObj) {//( event ) dispatched jQuery.Event
handleObj =types.handleObj;
jQuery(types.delegateTarget).off(
handleObj.namespace? handleObj.origType + "." +handleObj.namespace : handleObj.origType,
handleObj.selector,
handleObj.handler
);return this;
}if (typeof types === "object") {//( types-object [, selector] )
for (type intypes) {this.off(type, selector, types[type]);
}return this;
}if (selector === false || typeof selector === "function") {//( types [, fn] )
fn =selector;
selector=undefined;
}if (fn === false) {
fn=returnFalse;
}return this.each(function() {
jQuery.event.remove(this, types, fn, selector);
});
},
tigger方法,最终是调用event辅助类的tigger方法
trigger: function(type, data) {return this.each(function() {
jQuery.event.trigger(type, data,this);
});
},
triggerHandler方法,调用event类的方法
triggerHandler: function(type, data) {var elem = this[0];if(elem) {return jQuery.event.trigger(type, data, elem, true);
}
}
tiggerHandler和tigger方法的区别是,triggerHandler只执行jQuery对象数组中的第一个对象,并且不执行冒泡,不执行浏览器默认事件。