每个网站,交互的核心都是事件。浏览器环境一般提供两种事件绑定的方式:
一个是通过dom句柄的使用。如: element.onclick = function() {}
另一种就是事件委托了,如:docuement.addEventListener(element, function() {});
基于第二种,不同浏览器也有不用的api接口。这是需要注意的地方
当然,如果使用jquery,这些东西都不是你要关心的,他会替你解决一切。
jquery事件系统的强大是毋庸置疑的。那我们就来看看他到底强大在哪。
首先jquery事件系统是基于$.data缓存系统的,这个在先前的博文就有提及到。
jquery事件系统功能是非常强大的,所以它的块头也比较大。所以我们不能一口就把它给全吃掉。
我们以一定得顺序来探索jquery时间系统。我们首先看看jquery核心方法jquery.event.add,再由jquery.fn.on这个统一入口逐层分析整个事件系统。
让我们先来看看 jquery的核心绑定事件的方法。
jquery.event.add = function( elem, types, handler, data, selector ) {
var
// 该dom元素的缓存数据
elemData,
// 事件处理函数
eventHandle,
// 该元素对应的事件集合
events,
t, tns, type,
// 命名空间
namespaces,
// 处理对象
handleObj,
handleObjIn, handlers, special;
// 在不支持添加事件的元素上就直接返回
// Don't attach events to noData or text/comment nodes (allow plain objects tho)
// 在这一步中 通过jquery._data 获取了该元素对应的数据
if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
return;
}
// 调用者可以传递一个自定义数据的对象来替代处理方法
// Caller can pass in an object of custom data in lieu of the handler
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
selector = handleObjIn.selector;
}
// Make sure that the handler has a unique ID, used to find/remove it later
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
// 取出缓存对象中对应event事件的对象集合
// Init the element's event structure and main handler, if this is the first
events = elemData.events;
// 不存在则新建一个{}
if ( !events ) {
elemData.events = events = {};
}
// 事件处理函数
eventHandle = elemData.handle;
// 如果函数不存在 则指定一个函数
if ( !eventHandle ) {
elemData.handle = eventHandle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
// 事件调度方法
// 会根据元素的绑定的一些信息,找到对应缓存中的事件处理方法
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
eventHandle.elem = elem;
}
// Handle multiple events separated by a space
// 处理以空格隔开的多种事件,
// jQuery(...).bind("mouseover mouseout", fn);
types = jQuery.trim( hoverHack(types) ).split( " " );
for ( t = 0; t < types.length; t++ ) {
tns = rtypenamespace.exec( types[t] ) || [];
type = tns[1];
// 获取命名空间(这个是用来自定义事件的)
namespaces = ( tns[2] || "" ).split( "." ).sort();
// 如果事件改变了它的类型,则使用特殊事件处理方法
// If event changes its type, use the special event handlers for the changed type
special = jQuery.event.special[ type ] || {};
// 获取准确的事件类型
// If selector defined, determine special event api type, otherwise given type
type = ( selector ? special.delegateType : special.bindType ) || type;
// 更新special
// Update special based on newly reset type
special = jQuery.event.special[ type ] || {};
// handleObj is passed to all event handlers
handleObj = jQuery.extend({
type: type,
origType: tns[1],
data: data,
handler: handler,
guid: handler.guid,
selector: selector,
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
namespace: namespaces.join(".")
}, handleObjIn );
// 如果未绑定,则把事件绑定在开始获取的事件调度方法上,
// Init the event handler queue if we're the first
handlers = events[ type ];
if ( !handlers ) {
handlers = events[ type ] = [];
handlers.delegateCount = 0;
// Only use addEventListener/attachEvent if the special events handler returns false
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// Bind the global event handler to the element
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
} else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, eventHandle );
}
}
}
if ( special.add ) {
special.add.call( elem, handleObj );
if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
}
// 添加事件处理的对象,代理对象插入在队列前面
// Add to the element's handler list, delegates in front
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
}
// Keep track of which events have ever been used, for event optimization
jQuery.event.global[ type ] = true;
}
// Nullify elem to prevent memory leaks in IE
elem = null;
}