上班之余抽点时间出来写写博文,希望对新接触的朋友有帮助。今天在这里和大家一起学习一下事件数据
由于jQuery事件理管容内比较多,所以行进了分段,这篇文章要主讲的是事件的定绑。
jQuery.fn.on
在选择素元上定绑一个或多个事件的事件处置数函。
jQuery.fn.on = function( types, selector, data, fn, /*INTERNAL*/ one ) { var origFn, type; // types可是以一个由types/handlers成组的map对象 if ( typeof types === "object" ) { // 如果selector不是字符串 // 则将传参由( types-Object, selector, data )成变( types-Object, data ) if ( typeof selector !== "string" ) { data = data || selector; selector = undefined; } //历遍全部type for ( type in types ) { //添加type事件处置数函 this.on( type, selector, data, types[ type ], one ); } return this; } // 如果data为空,且fn为空 if ( data == null && fn == null ) { // 则传参由( types, selector )成变( types, fn ) fn = selector; data = selector = undefined; // 否则如果只是fn为空 } else if ( fn == null ) { // 如果selector为字符串 if ( typeof selector === "string" ) { // 则传参从( types, selector, data )成变( types, selector, fn ) fn = data; data = undefined; } else { // 否则传参从( type, selector, data )成变( types, data, fn ) fn = data; data = selector; selector = undefined; } } //……弄了半天其实就是在模拟重载而已……囧rz if ( fn === false ) { //如果fn为false则成变一个return false的数函 fn = returnFalse; } else if ( !fn ) { //如果fn当初还不存在,则直接return this return this; } // 如果one为1 if ( one === 1 ) { // 保存fn origFn = fn; // 重新义定fn fn = function( event ) { // 这个事件只用一次,用完就用off取消掉。 jQuery().off( event ); return origFn.apply( this, arguments ); } // 应用雷同的ID,为了来未好除删事件 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } // 对全部用jQuery.event.add来添加 return this.each( function() { jQuery.event.add( this, types, fn, data, selector ); }); };
文档中对selector的述描是:
一个选择器字符串用于过滤器的触发事件的选择器素元的后代。如果选择的< null或省略,当它达到选定的素元,事件老是触发。
A selector string to filter the descendants of the selected elements that trigger the event. If the selector is
null
or omitted, the event is always triggered when it reaches the selected element.说得貌似很悬乎,不过其实要主是在delegate定绑事件中去过滤素元的一些用不着的后代的。
jQuery.event.add
jQuery.event.add = function( elem, types, handler, data, selector ) { var handleObjIn, eventHandle, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, // 通过部内缓存获得素元数据 elemData = jQuery._data( elem ); // 不会没有数据或者text、comment点节添加事件 if ( !elemData ) { return; } // 如果handler是个包括handler和selector的对象 if ( handler.handler ) { // 则定位要必的数参 handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } // 如果handler没有ID,则给个ID给他 // 用于来未寻觅或者除删handler if ( !handler.guid ) { handler.guid = jQuery.guid++; } // 如果缓存数据中没有events数据 if ( !(events = elemData.events) ) { // 则初始化events events = elemData.events = {}; } // 如果缓存数据中没有handle数据 if ( !(eventHandle = elemData.handle) ) { // 义定事件处置数函 eventHandle = elemData.handle = function( e ) { // 取消jQuery.event.trigger第二次触发事件 // 以及装卸后的事件 return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; // 义定事件处置器对应的素元,用于止防IE非原生事件中的内存露泄 eventHandle.elem = elem; } // 事件是能可通过空格键隔分的字符串,所以将其成变字符串数组 types = ( types || "" ).match( core_rnotwhite ) || [""]; // 事件的度长 t = types.length; // 历遍全部事件 while ( t-- ) { // 试尝出取事件的namespace,如aaa.bbb.ccc tmp = rtypenamespace.exec( types[t] ) || []; // 出取事件,如aaa type = origType = tmp[1]; // 出取事件命名空间,如bbb.ccc,并根据"."隔分成数组 namespaces = ( tmp[2] || "" ).split( "." ).sort(); // 事件是不是会变改以后状态,如果会则应用特别事件 special = jQuery.event.special[ type ] || {}; // 根据是不是已义定selector,定决应用哪个特别事件api,如果没有非特别事件,则用type type = ( selector ? special.delegateType : special.bindType ) || type; // 更具状态变改后的特别事件 special = jQuery.event.special[ type ] || {}; // 组装用于特别事件处置的对象 handleObj = jQuery.extend({ type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join(".") }, handleObjIn ); // 初始化事件处置排队,如果是第一次应用 if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // 如果获得特别事件监听方法失败,则应用addEventListener行进添加事件,和attachEvent说88了 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } } } // 通过特别事件add处置事件 if ( special.add ) { // 添加事件 special.add.call( elem, handleObj ); // 设置处置数函的ID if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // 将事件处置数函推入处置列表 if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } // 表现事件曾应用过,用于事件化优 jQuery.event.global[ type ] = true; } // 设置为null免避IE中循环引用致导的内存露泄 elem = null; };
从这里的源代码看,
- 对于没有特别事件特有监听方法和普通事件都用addEventListener来添加事件了。
- 而又特有监听方法的特别事件,则用了另一种方式来添加事件。
special.add从2.0的源代码来看,似乎没用用到,看起来是遗留问题,来未也可以根据这个扩展事件模型。
jQuery.event.dispatch
我们先走第一个分支,也就是通过addEventListener触发jQuery.event.dispatch。
jQuery.event.dispatch = function( event ) { // 重写原生事件对象,成变一个可读写的对象,方便来未修改、扩展 event = jQuery.event.fix( event ); var i, j, ret, matched, handleObj, handlerQueue = [], // 把数参转成数组 args = core_slice.call( arguments ), // 从部内数据中查找该素元的对应事件处置器列表中的对应处置器,否则为空数组 handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], // 试尝将事件转成特别事件 special = jQuery.event.special[ event.type ] || {}; // 将数参数组第一个素元换成重写的事件对象 args[0] = event; event.delegateTarget = this; // 试尝应用特别事件的preDispatch钩子来定绑事件,并在要必时退出 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // 组装事件处置包{elem, handlerObjs}(这里是各种不同素元)的队列 handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // Run delegates first; they may want to stop propagation beneath us i = 0; // 历遍事件处置包{elem, handlerObjs}(出取来则对应一个包了),且事件不需要阻止冒泡 while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { // 义定以后Target为事件处置对象对应的素元 event.currentTarget = matched.elem; j = 0; // 如果事件处置对象{handleObjs}存在(一个素元可能有很多handleObjs),且事件不需要立刻阻止冒泡 while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { // 触发的事件必须满足其一: // 1) 没有命名空间 // 2) 有命名空间,且被定绑的事件是命名空间的一个子集 if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; // 试尝通过特别事件获得处置数函,否则应用handleObj中保存的handler(所以handleObj中还保存有handler) 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(); } } } } } // 试尝通过special.postDispatch勾住这个映射关系,来未可以化优 if ( special.postDispatch ) { special.postDispatch.call( this, event ); } // 返回事件数函 return event.result; };
这里有许多handle相关的东西,具体关系参见以下后面的数函。
jQuery.event.handlers
jQuery.event.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) // 如果有delegateCount,代表该事件是delegate类型的定绑 // 找出全部delegate的处置数函排队 if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { // 历遍素元及素元父级点节 for ( ; cur != this; cur = cur.parentNode || this ) { // 止防单机被禁用的素元时触发事件 if ( cur.disabled !== true || event.type !== "click" ) { // 开始组装符合要求的事件处置对象 matches = []; // 便利全部事件处置对象 for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) // 选择器,用于过滤 sel = handleObj.selector + " "; // 如果matches上没有定绑该选择器数量 if ( matches[ sel ] === undefined ) { // 在matches上定绑该选择器数量 matches[ sel ] = handleObj.needsContext ? // 得出选择器数量,并赋值 jQuery( sel, this ).index( cur ) >= 0 : jQuery.find( sel, this, null, [ cur ] ).length; } // 再次确定是不是定绑选择器数量 if ( matches[ sel ] ) { // 是则将事件处置对象推入 matches.push( handleObj ); } } // 如果得到的matches里有事件处置对象 if ( matches.length ) { // 组装成事件处置包(暂时这么叫吧),推入事件处置包队列 handlerQueue.push({ elem: cur, handlers: matches }); } } } } // 如果还有事件剩余,则将剩余的装包,推入排队 if ( delegateCount < handlers.length ) { handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); } return handlerQueue; }
从这里我们可以看出delegate定绑的事件和普通定绑的事件是如何分开的。
对应一个素元,一个event.type的事件处置对象队列在缓存里只有一个。
区分delegate定绑和普通定绑的方法是:delegate定绑从队列头部推入,而普通定绑从尾部推入,通过记录delegateCount来划分,delegate定绑和普通定绑。
special.setup
// 支持: Firefox 10+ // 创建冒泡的focus和blur事件,即focusin和focusout if ( !jQuery.support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { // 通过这个数参来记录某人focusin/focusout var attaches = 0, // handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); }; // 对需要修复的特别事件添加方法 jQuery.event.special[ fix ] = { setup: function() { if ( attaches++ === 0 ) { document.addEventListener( orig, handler, true ); } }, teardown: function() { if ( --attaches === 0 ) { document.removeEventListener( orig, handler, true ); } } }; }); }
第二个分支special.setup方法要主是来在Firefox中模拟focusin和focusout事件的,因为各大主流浏览器只有他不支持这两个事件。
由于这两个方法支持事件冒泡,所以可以用来行进事件代理。
jQuery.event.simulate
然后再利用jQuery.event.simulate来模拟事件触发。
jQuery.event.simulate = function( type, elem, event, bubble ) { // 重写事件 var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true, originalEvent: {} } ); // 如果要冒泡 if ( bubble ) { // 利用jQuery.event.trigger模拟触发事件 jQuery.event.trigger( e, null, elem ); } else { // 否则利用jQuery.event.dispatch来执行处置 jQuery.event.dispatch.call( elem, e ); } // 如果需要阻止默认操作,则阻止 if ( e.isDefaultPrevented() ) { event.preventDefault(); } }
jQuery.fn.one
jQuery.fn.one = function( types, selector, data, fn ) { return this.on( types, selector, data, fn, 1 ); };
运行一次就直接调用this.on,然后在最后数参设置成只用1次就行了。
jQuery.fn.bind
jQuery.fn.bind = function( types, data, fn ) { return this.on( types, null, data, fn ); };
这个也是通过this.on扩展的。
jQuery.fn.live
jQuery.fn.live = function( types, data, fn ) { jQuery( this.context ).on( types, this.selector, data, fn ); return this; };
jQuery.fn.delegate
jQuery.fn.delegate = function( selector, types, data, fn ) { return this.on( types, selector, data, fn ); };
可见jQuery.fn.on是事件添加的核心方法,几乎全部事件添加方法都是由这一方法扩展出来的。
文章结束给大家分享下程序员的一些笑话语录: 人脑与电脑的相同点和不同点,人脑会记忆数字,电脑也会记忆数字;人脑会记忆程序,电脑也会记忆程序,但是人脑具有感知能力,这种能力电脑无法模仿,人的记忆会影响到人做任何事情,但是电脑只有程序软件。比尔还表示,人脑与电脑之间最重要的一个差别就是潜意识。对于人脑存储记忆的特别之处,比尔表示,人脑并不大,但是人脑重要的功能是联络,人脑会把同样的记忆存储在不同的地方,因此记忆读取的速度就不相同,而这种速度取决于使用的频率和知识的重要性。人脑的记忆存储能力会随着年龄增长而退化,同时记忆的质量也会随着年龄退化。经典语录网