jQuery源码分析--event事件绑定(下)
来源:互联网 发布:在线抽奖软件 编辑:程序博客网 时间:2024/06/03 21:45
上文提到,
- 事件的绑定函数one(),bind(),delegate()等方法内部都是统一调用jQuery.fn.on()方法
- jquery.fn,on方法重载所有的输入,并调用jQuery.event.add方法,添加elemData属性(即把每个元素对应的事件缓存到一个缓冲区),包装事件,普通事件调用addeventlistener事件,回调depatch函数。特殊事件转交special.setup来处理。
具体代码如下:
// Only use addEventListener if the special events handler returns false //如果特殊事件returnfalse,获取失败,就用addEventListener方法啦 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } }
jQuery.event.special
//4582行special: { load: { // Prevent triggered image.load events from bubbling to window.load //防止window.load事件的继续冒泡,load事件不允许冒泡 noBubble: true }, focus: { // Fire native event if possible so blur/focus sequence is correct //如果可能的话立即执行原生事件,这样说明blur/focus事件是正确的 trigger: function() { if ( this !== safeActiveElement() && this.focus ) { this.focus(); //这里也取消冒泡和默认事件 return false; } }, delegateType: "focusin" }, blur: { trigger: function() { if ( this === safeActiveElement() && this.blur ) { this.blur(); return false; } }, delegateType: "focusout" }, click: { // For checkbox, fire native event so checked state will be right //对于checkbox标签,直接执行原生事件,这样选择状态就是对的 trigger: function() { if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { this.click(); return false; } }, // For cross-browser consistency, don't fire native .click() on links //为了跨浏览器的兼容性,不直接执行带link的click原生事件 _default: function( event ) { return jQuery.nodeName( event.target, "a" ); } }, beforeunload: { postDispatch: function( event ) { // Support: Firefox 20+ // Firefox doesn't alert if the returnValue field is not set. //兼容firefox 20+ //如果没有设置指定的字段是Firefox不告警。 if ( event.result !== undefined && event.originalEvent ) { event.originalEvent.returnValue = event.result; } } } },
special列举了一些特殊事件,处理兼容性的问题或特殊的实现。
special.setup
//4768行// Support: Firefox, Chrome, Safari// Create "bubbling" focus and blur events// 创建冒泡的focus和blur事件,即focusin和focusoutif ( !support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler on the document while someone wants focusin/focusout // 当需要focusin/focusout时,在文档上附上一个简单的捕获处理函数 var handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); }; // 对需要修复的特殊事件添加方法 jQuery.event.special[ fix ] = { setup: function() { var doc = this.ownerDocument || this, attaches = data_priv.access( doc, fix ); if ( !attaches ) { doc.addEventListener( orig, handler, true ); } data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); }, teardown: function() { var doc = this.ownerDocument || this, attaches = data_priv.access( doc, fix ) - 1; if ( !attaches ) { doc.removeEventListener( orig, handler, true ); data_priv.remove( doc, fix ); } else { data_priv.access( doc, fix, attaches ); } } }; });}
special.setup解决的问题
- 在Firefox中模拟focusin和focusout事件的,因为各大主流浏览器只有他不支持这两个事件。然后调用jQuery.event.simulate()方法来模拟事件触发。
- 对需要修复的特殊事件添加方法,即上文提到的special数组里的几种情况
jQuery.event.simulate
simulate: function( type, elem, event, bubble ) { // Piggyback on a donor event to simulate a different one. //搭载在一个新的捐赠事件上去触发另一个不同的事件 // Fake originalEvent to avoid donor's stopPropagation, but if the // simulated event prevents default then we do the same on the donor. 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(); } }
这里我们发现,这里针对冒泡事件是不冒泡事件分别用trigger事件来触发和depatch来触发。
jQuery.event.trigger
//4259行trigger: function( event, data, elem, onlyHandlers ) { var i, cur, tmp, bubbleType, ontype, handle, special, eventPath = [ elem || document ], type = hasOwn.call( event, "type" ) ? event.type : event, //命名空间的过滤 namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; cur = tmp = elem = elem || document; // Don't do events on text and comment nodes //在text或comment节点上阻止事件 if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } // focus/blur morphs to focusin/out; ensure we're not firing them right now //focus/blur 变成 focusin/out,保证我们现在不会触发它们 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } if ( type.indexOf(".") >= 0 ) { // Namespaced trigger; create a regexp to match event type in handle() //命名空间的触发,创建一个正则表达式去匹配事件类型 namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } ontype = type.indexOf(":") < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string //包装event事件 event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) event.isTrigger = onlyHandlers ? 2 : 3; event.namespace = namespaces.join("."); event.namespace_re = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : null; // Clean up the event in case it is being reused //清理事件防止重复使用 event.result = undefined; if ( !event.target ) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list //克隆任何传入的数据和预先设置的事件,创建处理程序列表 data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines //允许特殊事件 special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) //对每一个W3C事件,提前确定事件的传播路径 // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) //冒泡到document再到文档,看全局var变量?? if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } for ( ; cur; cur = cur.parentNode ) { eventPath.push( cur ); tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === (elem.ownerDocument || document) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } //对 eventPath 进行模拟冒泡的触发 // Fire handlers on the event path i = 0; while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // Native handler handle = ontype && cur[ ontype ]; if ( handle && handle.apply && jQuery.acceptData( cur ) ) { event.result = handle.apply( cur, data ); if ( event.result === false ) { event.preventDefault(); } } } event.type = type; // If nobody prevented the default action, do it now //如果没有人阻止错误行为,就执行 if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && jQuery.acceptData( elem ) ) { // Call a native DOM method on the target with the same name name as the event. //如果事件名字一样,对target调用原生DOM方法 // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method 不要重复触发 tmp = elem[ ontype ]; if ( tmp ) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; elem[ type ](); jQuery.event.triggered = undefined; if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; },
其实还是没太看懂。但是trigger 的处理就是模拟冒泡的一个调度,具体的触发还是交给 jQuery.event.dispatch 方法了。
trigger函数具体工作如下:
- 命名空间的过滤
- 模拟事件对象
- 制作一个触发的路径队列eventPath
- 对 eventPath 进行模拟冒泡的触发
- 在一个层级调用 dispatch 处理各自的内部事件关系(委托)
从这一个功能点上我们就不难发现为什么 jQuery 要设计元素与数据分离了。
- 如果是直接绑定的话就完全无法通过 trigger 的机制调用。trigger
的实现首先得益于事件的分离机制,因为没有直接把事件相关的与元素直接绑定采用了分离处理,所以我们通过 trigger 触发addEventListener 触发的处理流程都是一致的,不同的只是触发的方式而已。 - 通过 trigger触发的事件是没有事件对象、冒泡这些特性的,所以我们需要有一个方法能模拟出事件对象,然后生成一个遍历树模拟出冒泡行为,那么这个任务就交给了trigger 方法了。
0 0
- jQuery源码分析--event事件绑定(下)
- jQuery源码分析--event事件绑定(上)
- jQuery源码分析之Event事件分析
- jQuery源码分析之Event事件分析
- jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/undelegate
- jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/unde
- jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/undelegate
- jQuery源码分析-10事件处理-Event-源码结构
- jQuery源码分析-10事件处理-Event-DOM-ready
- jQuery源码分析-10事件处理-Event-DOM-ready
- jQuery源码分析-10事件处理-Event-概述和基础知识
- jQuery源码分析-10事件处理-Event-DOM-ready
- 事件触发jQuery event(下)
- jQuery-1.9.1源码分析系列(十) 事件系统——事件绑定
- nova event事件源码分析
- jquery事件绑定解绑机制源码分析
- jQuery Event add [ 源码分析 ]
- nginx源码分析--event事件驱动初始化
- UIApplicationDelegate-UI进阶
- android开发笔记之多媒体—撕衣服(开心一下)
- hdu 2955 Robberies
- 4. Median of Two Sorted Arrays
- iOS基础开发UI界面-xib与代码自定义控件的区别
- jQuery源码分析--event事件绑定(下)
- BestCoder Round #83 1001 zxa and set
- POJ 2229 Sumsets
- uva10651 Pebble Solitaire(记忆化搜索)
- iOS应用程序的启动原理-UI进阶
- 第七周项目一-成员函数、友元函数和一般函数有区别
- tomcat JVM 性能优化
- 【3325】顺序表应用2:多余元素删除之建表算法
- 【步兵 cocos2dx】四叉树碰撞算法