<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Event fun</title> </head> <body> <div style="width:200px;height:600px;background-color: red;"> <div style="width:200px;height:500px;background-color: blue;"> <div style="width:200px;height:400px;background-color: green;"> <div id="a" style="width:200px;height:300px;background-color: black;"> <div style="width:200px;height:200px;background-color: yellow;"> </div> </div> </div> </div> </div> <script src="../../jquery-2.1.1.js"></script> <script> $(document).on(‘click‘,function(){}) $(document).on(‘click‘,‘#a‘,function(){}) $(document).on(‘click‘,‘div‘,{ name:‘qq‘, age:‘dd‘ },function(){ console.log(1);//5次 }) var doc = $(document) document.onclick = function(e){ console.log(1);//1次 } </script> </body> </html>
在这个有5层的html嵌套结构中,当在最内部的div上点击一下。jQuery的事件代理会触发5次。元素的会触发1次。
上源码:
handlers: function( event, handlers ) {//新的事件对象 , 该类型的监听对象数组。将当前元素的所有监听事件排成一个序列,从底到顶,然后是普通事件 var i, matches, sel, handleObj, handlerQueue = [],//响应对象数组 delegateCount = handlers.delegateCount,//代理事件的数量 cur = event.target;//目标元素 // Find delegate handlers // Black-hole SVG <use> instance trees (#13180) // Avoid non-left-click bubbling in Firefox (#3861) if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { for ( ; cur !== this; cur = cur.parentNode || this ) {//从触发了事件的目标元素,向上找,一直到代理的元素 // Don‘t process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.disabled !== true || event.type !== "click" ) {//排除不支持click的元素 matches = []; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don‘t conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; if ( matches[ sel ] === undefined ) {//判断目标元素是否匹配selector的过滤 matches[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) >= 0 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matches[ sel ] ) {//匹配的话将响应对象入队。 matches.push( handleObj ); } } if ( matches.length ) {//如果匹配,将元素和响应对象序列入数组 handlerQueue.push({ elem: cur, handlers: matches }); } } } } // Add the remaining (directly-bound) handlers if ( delegateCount < handlers.length ) {//绑定了普通的事件,入数组 。 handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); } return handlerQueue; },
接下来就是这个dispatch了,其实也很简单,就干了3样事情。
1、检查是不是特殊的事件,如果是优先使用特殊的处理函数
2、调用函数传入jQuery事件对象。这就是为什么在处理函数内部的事件对象是jQuery的事件对象了。
3、检查上函数的返回值是不是为false,如果是就调用jQuery.Event对象的原型中的方法阻止冒泡和默认行为。
上源码:
dispatch: function( event ) {//event为原生事件对象 函数作用:主监听函数 // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event );//创建事件对象 var i, j, ret, matched, handleObj,//ret返回值,matched放置匹配过的响应对象 handlerQueue = [],//待执行队列。包括后代元素匹配的代理监听对象数组 和 当前元素上绑定的普通监听对象数组。 args = slice.call( arguments ),//把arguments转换成真正的数组 handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],//当前事件类型对应的监听对象数组 special = jQuery.event.special[ event.type ] || {};//获取事件的修正对象 // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event;//存储事件对象 event.delegateTarget = this;//代理对象 // Call the preDispatch hook for the mapped type, and let it bail if desired if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // Determine handlers handlerQueue = jQuery.event.handlers.call( this, event, handlers );//确定要执行的响应函数数组 // Run delegates first; they may want to stop propagation beneath us i = 0; while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {//队列中还有元素且没有被阻止冒泡 event.currentTarget = matched.elem;//当前的元素 j = 0; while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {//该元素上有响应对象且没有被阻止 // Triggered event must either 1) have no namespace, or // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {//没有传入命名空间,或者命名空间匹配 event.handleObj = handleObj;//复制 event.data = handleObj.data; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args );//优先调用修正事件处理函数,将返回值存在 if ( ret !== undefined ) {//返回值为false是,阻止冒泡和默认行为 if ( (event.result = ret) === false ) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) {//beforeUnload special.postDispatch.call( this, event ); } return event.result; },
off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) {//使用dispatched分发过的jquery处理函数对象,也就是事件正在被触发 // ( 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 是对象,用于一次性移除多个事件类型和过个监听函数 // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) {//修正参数,selector为false,或者只传入两个参数 // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) {//没传fn fn = returnFalse; } return this.each(function() {//调用remove删除事件 jQuery.event.remove( this, types, fn, selector ); }); }, remove: function( elem, types, handler, selector, mappedTypes ) { var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = data_priv.hasData( elem ) && data_priv.get( elem ); if ( !elemData || !(events = elemData.events) ) {//没有关联的缓存数据或者事件缓存对象 return; } // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match( rnotwhite ) || [ "" ];//转换成数组 t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) {//如果没有指定事件类型,则移除元素所有事件或命名空间中所有事件(有给定命名空间的情况) for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {};//获得修正对象(如果有) type = ( selector ? special.delegateType : special.bindType ) || type;//如果有传selector则修正为代理事件,否则优先考虑修正为更好的事件 handlers = events[ type ] || []; tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );//用于检测已绑定事件和types的命名空间是否一样 // Remove matching events origCount = j = handlers.length; while ( j-- ) { handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) &&//mappedTypes不为真时比较传入类型和监听对象的原始事件类型 ( !handler || handler.guid === handleObj.guid ) &&//没有指定监听函数 或 指定监听函数与监听对象具有一样的id ( !tmp || tmp.test( handleObj.namespace ) ) &&//没有指定命名空间或者监听对象的命名空间具有指定的命名空间 ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {//没传入selector 或 有传入但是与监听对象的相等 或 为"**"(所有)是监听对象有selector handlers.splice( j, 1 );//从监听对象数组中删除 if ( handleObj.selector ) {//如果删除了的是代理事件 则修正dele gateCount 以便下一次插入代理事件的正确 handlers.delegateCount--; } if ( special.remove ) {//有对应的修正方法remove,则调用 special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) {//某类型事件监听对象数组被清空,删除主监听函数 if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {优先调用teardown移除主监听函数 jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ];//从总监听对象数组中删除该类型的监听对象数组 } } // Remove the expando if it‘s no longer used if ( jQuery.isEmptyObject( events ) ) {//总监听对象数组为空,说明该元素上的所有事件都被移除 delete elemData.handle;//移除主监听函数储存数据的对象 data_priv.remove( elem, "events" );//移除缓存 } },
原文:http://www.cnblogs.com/dq-Leung/p/4340106.html