本课还是来讲解一下jQuery是如何实现它的事件系统的。这一课我们先来讲一下jQuery.event.remove的源码解读。

remove方法的目的是,根据用户传参,找到事件队列,从里面把匹配的handleObj对象移除,在参数不足的情况下,可能移除多个或所有的handleObj。当队列的长度为0(当某事件的事件处理函数数组为空时,就代表此类型事件没有事件处理函数了,因此移除此事件)时,就移除相应的事件,当events为空对象(events是元素绑定的所有事件类型的集合,当它为空时,就代表元素没有绑定任何事件,那么就没有必要再保存元素在缓存系统的唯一标识了)时,就清除掉UUID(也就是元素在缓存系统中唯一的标识)。

接下来,我们来看它的源码:

remove = function(elem,types,handler,selector){

  var t,tns,type,origType,namespaces,origCount,j,events,special,eventType,handleObj,

    elemData = jQuery.hasData(elem) && jQuery._data(elem);

  if(!elemData || !(events = elemData.events)){   //如果缓存系统中没有有关这个元素的数据,或者不支持添加自定义属性(jQuery._data(elem))就直接返回。如果支持添加自定义属性,并且缓存系统中有这个元素的数据,但如果有关这个元素的事件处理函数为空,也直接返回,因为没有什么可以移除了,此元素本身就没有绑定任何事件处理函数。

    return;

  }

  types = jQuery.trim(hoverHack(types||"")).split(" ");   //处理hover事件的,它需要转换成mouseenter和mouseleave两个事件

  for(t=0;t<types.length;t++){

    ....

    type = origType = types[t];

    ....

    if(!type){   //如果没有指定需要移除的事件类型,那么就移除所有事件类型或所有与此命名空间有关的事件类型

      for( type in events){

        jQuery.event.remove(elem,type,handler,selector,true);

      }

      continue;

    }   

    special = jQuery.event.special[type] || {}; //处理特殊的事件类型,并不是所有的事件都能直接使用,比如火狐下没有mousewheel,需要用DOMMouseScroll冒充

    type = (selector ? special.delegateType: special.bindType) || type; //对于事件代理的绑定,需要针对不能冒泡的事件进行处理,取得真正用于绑定的事件类型

    eventType = events[type] || []; //取得此事件类型的装载事件描述对象的数组

    origCount = eventType.length;

    for(j=0;j<eventType.length;j++){   //移除数组中符合条件的事件描述对象

      handleObj = eventType[j];

      if(origType===handleObj.origType&&   //比较事件类型是否一致

        !handler || handler.guid === handleObj.guid&&  //如果没有传入事件处理函数,或者传入了,并且guid等于事件描述对象的guid,就继续判断

          !namespace || namespace.test(handleObj.namespace) && //如果types有命名空间,就判断他们的命名空间是否一样

            !selector || selector === handleObj.selector //如果是事件代理,就判断事件代理是否一致,

      ){

        eventType.splice(j--,1);   //移除这个事件描述对象

        if(handleObj.selector){   //如果是事件代理的事件描述对象被移除,就把delegateCount减一

          eventType.delegateCount--;

        }

        .....

      }

    }

    if(eventType.length === 0 && origCount ! ==eventType.length){//如果事件描述对象数组为空,并且是因为上面for循环移除之后才为空的,就进入if语句。之所以加上后面的那个判断,是因为eventType本身如果是空的,并不是通过for循环移除的,那进入到if语句,会出现死循环

      .....

      delete events[type];   //删除此类型事件的属性。因此此类型事件没有事件处理函数了,被全部删除了

    }

  }    

  if(jQuery.isEmptyObject(events)){//如果此元素的所以事件类型的属性都被删除了,那么events将是一个空对象

      delete elemData.handle;     //既然此元素的所有事件类型都没有了,就证明此元素没有绑定任何事件,那么也要删除通过add添加给它的回调方法(此回调方法会调用此元素的所有回调方法)。

      jQuery.removeData(elem,"events",true);   //移除这个元素在缓存系统中的events空间(清楚这个元素在缓存空间的UUID)。

  }

}

接下来,我们来讲一下jQuery.event.dispatch的源码:

jQuery就是利用这个dispatch方法,从缓存体中的events对象取得对应队列,然后修复事件对象event,传入用户的回调中执行,根据回调的返回值决定是否停止循环,是否阻止默认行为和事件冒泡。

dispatch = function(event){

  event = jQuery.event.fix(event || window.event);    //解决W3C和IE事件对象的差异,比如:W3C,event.target等于IE的event.srcElement

  var i,j,cur,ret,selMatch,matched,matched,handleObj,sel,related,

    handlers = ((jQuery._data(this,"events") || {}) [event.type]  || [],  //得到缓存系统中this(操作的元素)的events对象,然后取它的事件类型(你触发的event的类型)属性对象,这个属性对象就是装有事件描述对象的数组。

      delegateCount = handlers.delegateCount,  //得到这个事件描述对象数组有多少个使用了事件代理

        args = core_slice.call(arguments),   //[].slice.call(arguments),转换成数组

          ...

          special = jQuery.event.special[event.type]  || {},   //解决特殊的事件类型

            handlerQueue = [];

  ....

  if(delegateCount && !(event.button && event.type === "click")){  //这里先讲一下event.button这个属性,这个属性,代表用户点击了鼠标的左键还是右键,还是滚动条,等等。IE与标准浏览器的值不一样,比如,左键IE下是1,而其他浏览器是0等。但是在上面的代码中已经修复了event对象,因此IE下,点击左键也就是0.因此上面的if语句的意思是:如果有事件代理,并且是左键点击(event.button=0),就进入if语句。

    for(cur = event.target ; cur != this;cur = cur.parentNode || this){ //从事件源开始,遍历其所有的祖先,一直到绑定事件的元素

      if(cur.disabled !==true || event.type !== click){//如果元素是被禁止的,并且事件类型是click,就不进入if语句。意思就是不能在被禁止的元素上触发click事件

        ....

        matches = []; //用于收集符合条件的事件描述对象

        for(i=0;i<delegateCount;i++){  //使用事件代理的事件描述对象总是排在数组前面

          handleObj = handlers[i];

          sel = handleObj.selector;  //获取这个事件描述对象的执行上下文

          if(selMatch[sel] ===undefined) {  //第一次时,selMatch[sel]是undefined

            selMatch[sel] =  jQuery.find(sel,this,null,[cur]).length;  //sel,当前事件描述对象的执行上下文,this事件触发的元素,cur当前遍历的元素。如果cur当前遍历的元素匹配,就为真,不匹配就为假

          }

          if(selMatch[sel]){  //如果当前元素在事件描述对象中匹配

            matches.push(handleObj);  //就把这个事件描述对象收集起来。有多少元素在事件描述对象数组中匹配,就收集多少个事件描述对象。

          }

        }

        if(matches.length){

          handlerQueue.push({elem:cur,matches:matches});//如果当前遍历的元素,匹配事件描述对象数组中的一个或以上。就把这些组装成一个json对象存储起来。

        }

      }

    }   

  } 

  if(handlers.length > delegateCount){   //如果有不是事件代理的事件描述对象,将把这些事件描述对象与绑定事件的元素组装成json,存储起来

    handlerQueue.push({elem:this,matches:handlers.slice(delegateCount)});

  } 

  for(i=0;i<handlerQueue.length && !event.isPropagationStopped();i++){   //遍历handlerQueue数组,条件是event没有停止冒泡。

    matched = handlerQueue[i];

    event.currentTarget = matched.elem;  //事件冒泡到的当前目标元素。这里的currentTarget跟原生js绑定事件的currentTarget(绑定事件的元素)不一样,这里相当于原生的target(实际触发事件的元素)

    for(j=0;j<matched.matches.length && !event.isImmediatePropagationStopped();j++){//遍历当前目标元素中所有与event.type同类型的事件描述对象数组。条件是用户没有执行停止冒泡的方法。(用户调用stopImmediatePropagation方法,isImmediatePropagationStopped方法就会返回true)

      handleObj = matched.matches[j] ;   //取到这个事件描述对象

      .....

      event.data = handleObj.data;

      event.handleObj = handleObj;   //把事件描述对象赋给事件的handleObj属性

      ret =( (jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler ).apply(matched.elem,args) ;// 先看此事件描述对象中的事件类型是否是特殊事件,如果是,就调用它的handle方法。如果不是,就直接调用事件描述对象的handler方法。执行上下文是在当前遍历的目标元素中。

      if(ret!==undefined){

        event.result = ret;  //如果事件处理函数执行后,返回值不是undefined,就赋给event的result属性

        if(ret === false){  //如果事件处理函数返回false

          event.preventDefault();   //阻止默认事件

          event.stopPropagation();  //停止冒泡,isPropagationStopped会返回true,这时就会停止外层循环

        }

      }

    }

  }

  return event.result;   //返回事件处理函数执行后的返回值。

} 

dispatch的难点在于,模拟事件传播的机制,其实就是事件冒泡的机制。 

stopImmediatePropagation:function(){

  this.isImmediatePropagationStopped = function(){ return true };   //内循环终止

  this.stopPropagation();   //外循环终止

}

这个方法会让内循环和外循环都终止。

加油!

第二十四课:jQuery.event.remove,dispatch的源码解读的更多相关文章

  1. NeHe OpenGL教程 第二十四课:扩展

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. 第二十四篇 jQuery 学习6 删除元素

    jQuery 学习6 删除元素   上节课我们做了添加元素,模拟的是楼主发的文章,路人评论,那么同学们这节课学了删除之后,去之前的代码上添加一个删除,模拟一个楼主删除路人的评论. jQuery的删除方 ...

  3. ThinkPHP第二十四天(JQuery常用方法、TP自动验证)

    ---恢复内容开始--- 1.JQuery常用方法 A:JS中可以用json格式数据当做数组使用,如var validate={username:false,pwd:false,pwded:false ...

  4. 潭州课堂25班:Ph201805201 django 项目 第二十四课 文章主页 多级评论数据库设计 ,后台代码完成 (课堂笔记)

    加载新闻评论功能 1.分析 业务处理流程: 判断前端传的新闻id是否为空,是否为整数.是否不存在 请求方法:GET url定义:'/news/<int:news_id>' 请求参数:url ...

  5. python第二十四课——set中的函数

    集合中常用的一些函数: 1.add(obj):追加一个obj元素到集合中 pop():从集合中随机弹出一个元素 remove(obj):删除集合中和obj匹配的元素 clear():清空集合 s1={ ...

  6. Spring入门第二十四课

    Spring对JDBC的支持 直接看代码: db.properties jdbc.user=root jdbc.password=logan123 jdbc.driverClass=com.mysql ...

  7. Python学习第二十四课——Mysql 外键约束

    外键:主要是关联两个表的 举个栗子:在建表中创建外键 -- 添加外键例子 CREATE TABLE teacher( id TINYINT PRIMARY KEY auto_increment, na ...

  8. arcgis api 3.x for js 入门开发系列十四最近设施点路径分析(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  9. 设计模式(二十四)——职责链模式(SpringMVC源码分析)

    1 学校 OA 系统的采购审批项目:需求是 采购员采购教学器材 1) 如果金额 小于等于 5000,  由教学主任审批 (0<=x<=5000) 2) 如果金额 小于等于 10000,   ...

随机推荐

  1. python 发送邮件函数模块

    发送邮件函数功能 #!/usr/bin/env python # -*- coding:utf-8 -*- import smtplib from email.mime.text import MIM ...

  2. table边框美化

    在<table>里面加代码就可以了. 效果1: <TABLE style="BORDER-RIGHT: #993333 3px dashed; BORDER-TOP: #9 ...

  3. Combine small files to Sequence file

    Combine small files to sequence file or avro files are a good method to feed hadoop. Small files in ...

  4. Linux 系统常用命令汇总(二) vi 文本编辑

    文本编辑 vi 命令 作用 +文件名 编辑文本文件,若文件不存在同时创建该文件 Ctrl+f 向后翻一页 Ctrl+b 向前翻一页 Ctrl+d 向后翻半页 Ctrl+u 向前翻半页 + 光标移动到下 ...

  5. bzoj-2748 2748: [HAOI2012]音量调节(dp)

    题目链接: 2748: [HAOI2012]音量调节 Time Limit: 3 Sec  Memory Limit: 128 MB Description 一个吉他手准备参加一场演出.他不喜欢在演出 ...

  6. 实习小记-python 内置函数__eq__函数引发的探索

    乱写__eq__会发生啥?请看代码.. >>> class A: ... def __eq__(self, other): # 不论发生什么,只要有==做比较,就返回True ... ...

  7. ZOJ 3233 Lucky Number --容斥原理

    这题被出题人给活活坑了,题目居然理解错了..哎,不想多说. 题意:给两组数,A组为幸运基数,B组为不幸运的基数,问在[low,high]区间内有多少个数:至少被A组中一个数整除,并且不被B中任意一个数 ...

  8. WEB安全测试之XSS攻击

    目录结构 1.背景知识 2.XSS漏洞的分类 3.XSS防御 4.如何测试XSS漏洞 5.HTML Encode 6.浏览器中的XSS过滤器 7.ASP.NET中的XSS安全机制 一.背景知识 1.什 ...

  9. 程序清单8-3 8-4 演示不同的exit值

    //http://blog.chinaunix.net/uid-24549279-id-71355.html /* ========================================== ...

  10. HTML5 web workes实现多线程

    对多线程来说尽量使用HTML5的WEB WORKER特性 HTML5中的Web Worker是使用多个线程并发执行Javascript程序.另外,这种特别的多线程实现能减少困惑开发者多年的,在其他平台 ...