jQuery 1.9.1源码分析系列(十)事件系统之主动触

发现一个小点,先前没有注意的

stopPropagation: function() { var e = this.originalEvent; ... if ( e.stopPropagation ) { e.stopPropagation(); }

  jQuery重载stopPropagation函数调用的本地事件对象的stopPropagation函数阻止冒泡。也就是说,阻止冒泡的是当前节点,而不是事件源。

  说到触发事件,我们第一反应是使用$(...).click()这种方式触发click事件。这种方式毫无疑问简洁明了,如果能使用这种方式推荐使用这种方式。但是如果是自定义事件呢?比如定义一个$(document).on("chuaClick","#middle",fn);这种情况怎么触发事件?这就要用到$("#middle").trigger("chuaClick")了。

a.触发事件低级API——jQuery.event.trigger

  trigger函数对所有类型事件的触发提供了支持。这些事件主要分为两类:普通浏览器事件(包含带有命名空间的事件如"click.chua")、自定义事件。因为要统一处理,所以函数内部实现没有调用.click()这种方式来对普通浏览器事件做捷径处理,而是统一流程。处理过程如下

  1.获取要触发的事件(传入的event可能是事件类型而不是事件对象)

event = event[ jQuery.expando ] ? event :new jQuery.Event( type, typeof event === "object" && event );

  2.修正浏览器事件(主要有修正事件源)和组合正确的事件处理参数data

if ( type.indexOf(".") >= 0 ) { //有命名空间的事件触发; 先取出事件处理入口函数handle()使用的事件类型type namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } ...// 调用者可以传递jQuery.Event对象,普通对象,甚至是字符串 event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); event.isTrigger = true; event.namespace = namespaces.join("."); event.namespace_re = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : null; // 重置result属性,避免上次的结果残留 event.result = undefined; if ( !event.target ) { event.target = elem; } // 克隆传参data并将event放在传参data的前面,创建出事件处理入口函数的参数列表,创建后结果可能是[event,data] data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] );

  后面这段组合事件处理参数列表data在后面处理时调用

if ( handle ) { handle.apply( cur, data ); }

  3.判断是否是特殊节点对象的的特殊事件,是的话特殊处理

 special = jQuery.event.special[ type ] || {};   if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {     return;   }

  这里面需要特殊处理的事件比较少,这里列一下

special: { click.trigger: function(){ // checkbox, 触发本地事件确保状态正确if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { this.click(); return false; } }, focus.trigger: function() { // 触发本地事件保证失焦/聚焦序列正确if ( this !== document.activeElement && this.focus ) { try { this.focus(); return false; } catch ( e ) { // Support: IE<9 // If we error on focus to hidden element (#1486, #12518), // let .trigger() run the handlers } } }, blur.trigger: function() {if ( this === document.activeElement && this.blur ) { this.blur(); return false; } }     }

  4.从事件源开始遍历父节点直到Window对象,将经过的节点保存(保存到eventPath)下来备用

for ( ; cur; cur = cur.parentNode ) {   eventPath.push( cur );   tmp = cur; } // 将window也压入eventPath(e.g., 不是普通对象也不是断开连接的DOM) if ( tmp === (elem.ownerDocument || document) ) {   eventPath.push( tmp.defaultView || tmp.parentWindow || window ); }

  5.循环先前保存的节点,访问节点缓存,如果有对应的事件类型处理队列则取出其绑定的事件(入口函数)进行调用。      

// jQuery绑定函数处理:判断节点缓存中是否保存相应的事件处理函数,如果有则执行 handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // 本地绑定处理 handle = ontype && cur[ ontype ]; if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { event.preventDefault(); }

  6. 最后处理浏览器默认事件,比如submit标签的提交表单处理。

// 如果没有人阻止默认的处理,执行之 if ( !onlyHandlers && !event.isDefaultPrevented() ) { ... }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgzdxz.html