每个网站,交互的核心都是事件。浏览器环境一般提供两种事件绑定的方式:
一个是通过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; }