zoukankan      html  css  js  c++  java
  • 第二十五课:jQuery.event.trigger的源码解读

    本课主要来讲解jQuery.event.trigger的源码解读。

    trigger = function(event, data, elem, onlyHandlers){

      if(elem && (elem.nodeType === 3 || elem.nodeType ===8)){  //触发的元素节点不能是文本节点和注释节点

        return;

      }

      var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType, type = event.type || event , namespaces =[];

      .....

      if(type.indexOf(".") >= 0 ){  //如果事件类型带点号,就代表有命名空间,所以需要分解出命名空间

        namespaces = type.split(".");

        type = namespaces.shift();

        namespaces.sort();

      }

      if((!elem || jQuery.event.customEvent [ type ]) && !jQuery.event.global[type]){  //如果元素不存在或者事件类型是自定义事件,并且之前从来没有绑定过此类型的事件,那么就直接返回。

        return;

      }

      ....

      ontype = type.indexOf(":") < 0 ? "on" + type : ""; //如果事件类型没有:字符,就证明是ontype绑定事件

      if(!elem){   //如果没有指明触发元素,就把整个缓存系统找一遍

        cache = jQuery.cache;

        for(i in cache){

          if(cache[i].events && cache[i].events[ type ]){   //从缓存系统中,找相对应的events属性,并找到要触发事件类型的events[type]

            jQuery.event.trigger(event,data,cache[i].handle.elem,true);     //触发所有绑定了此事件类型的回调方法。

          }

        }

        return;   //直接返回

      }

      event.result = undefined;//清掉event的result属性,方便重复使用。

      if(!event.target){

        event.target = elem;

      }

      data = data!=null ? jQuery.makeArray(data) :[];   //如果传入了参数,就把参数转换成数组类型

      data.unshift(event);   //把事件对象放入数组的第一项

      special = jQuery.event.special[type] || {};   //事件类型是否需要进行特殊化处理,比如:mousescroll事件

      if(special.trigger && special.trigger.apply(elem,data) === false){  //如果事件类型已经有trigger方法,就调用它,如果返回结果是false,就直接返回。

        return;

      }

      eventPath = [[elem, special.bindType || type]];  //规划冒泡的路径,从当前元素,一直到window

      if(!onlyHandlers && !special.noBubble && !jQuery.isWindow(elem)){ //如果元素的事件类型没有阻止冒泡,并且元素不是window对象,就要进行冒泡模拟。

        bubbleType = special.delegateType || type;    //得到事件的类型

        cur = elem.parentNode;

        for(old = elem; cur ; cur = cur.parentNode){   //把当前元素的所有父元素,都进行一次事件类型的冒泡。假设div1(当前元素,触发click事件)上面有一个div2,div2上面有body,body上面有html,html上面有document。那么eventPath的数组是:[[div1,click],[div2,click],[body,click],[html,click],[document,click]]

          eventPath.push([cur, bubbleType]);

          old = cur;

        }

        if(old === (elem.ownerDocument || document)){   //当old为document时,cur为空,就退出循环

          eventPath.push([old.defaultView || old.parentWindow || window, bubbleType]);   //模拟冒泡到window对象

        }

      }

      for(i=0;i<eventPath.length&&!event.isPropagationStopped(); i++){ //沿着上面规划好的冒泡路线,把经过的元素节点的指定类型事件的回调逐一触发执行

          cur = eventPath[i][0];   //元素

          event.type = eventPath[i][1]   //事件类型

          handle = (jQuery._data(cur,"events") || {})[event.type] && jQuery._data(cur,"handle");//先判断在缓存系统中是否有此元素绑定的此事件类型的回调方法,如果有,就取出来。

          if(handle){   //执行这些回调方法

            handle.apply(cur,data);

          }

          handle = ontype && cur[ontype];    //如果有onXXX绑定的回调,无论是写在js中,还是html标签内,都会取到

          if(handle && jQuery.acceptData(cur) && handle.apply && handle.apply(cur, data) === false){//如果handle不为空,并且当前元素能绑定数据,并且handle有apply方法,就执行handle回调方法,如果当前回调返回false,就阻止默认事件。

            event.preventDefault();       

          }

      }

      event.type  = type;

      if(!onlyHandlers &&  !event.isDefaultPrevented()){  //如果没有执行preventDefault方法,或上面的handle方法返回false(也会阻止默认事件)。就模拟默认行为。具体是指模拟:submit,blur,focus,select,reset,scroll等方法。

        if((!special._default || special._default.apply(elem.ownerDocument,data) === false) && //如果用户指定了默认行为,就执行指定的默认行为,如果指定的默认行为返回false,就继续判断下面的条件

            !(type === "click" && jQuery.nodeName(elem, "a"))&&    //如果事件类型是click,并且是在a标签上触发的,就不执行它的默认行为了,否则继续判断

              jQuery.acceptData(elem)){   //如果当前元素可以绑定数据

                if(ontype && elem[type] &&  //如果元素有onXXX回调,并且元素有type方法,比如:<input type ="submit" onsubmit="function(){}">,它有onXXX的回调方法,也有input.submit的方法,因此继续判断

                  ((type!=="focus" && type!=="blur") || event.target.offsetWidth !==0) && //如果事件类型不是focus,blur。或事件类型是focu,blur,但是触发这个事件的元素不是隐藏的(隐藏的元素,它的offsetWidth为0),就继续判断。(触发隐藏元素的focus和blur默认行为,IE6-8下会抛出错误。)

                    !jQuery.isWindow(elem)){     //不是window元素就进入if语句执行默认行为。(window的默认行为触发,会出现问题,比如:window.scroll方法,在IE与标准浏览器存在差异,IE会默认scroll()方法为scroll(0,0))

                      old = elem[ontype];

                      if(old){

                        elem[ontype] = null;    //onXXX的回调已经执行过了,就不用再次执行了

                      }

                      jQuery.event.triggered = type;   //标识正在触发此事件类型,防止下面的elem [ type ] ()重复执行dispatch方法

                      elem [ type ] ();   //执行默认行为,比如:input[submit](),input元素的提交方法。点击类型为submint的input,会默认执行submit属性方法。但是这里不会调用dispatch方法。  

                      jQuery.event.triggered = undefined;  //还原

                      if(old){

                        elem[ontype] = old;

                      }

                }

        }

      }

      return event.result;

    }

    笼统来说,trigger就是dispatch的加强版。dispatch只触发当前元素与其底下元素(通过事件代理的方式)的回调。trigger则是模拟整个冒泡过程,除了它自身,还触发其祖先节点与window的同类型的回调。不过从trigger的代码来看,它比dispatch多做的事就是触发事件的默认行为。其实trigger要做的事就是在某一元素触发一个回调(dispatch),然后让它顺势冒泡,触发其他回调(dispatch)就行了。

    浏览器提供了原生派发机制,IE下叫fireEvent,标准浏览器为dispatchEvent。IE下的问题是,有许多事件不能冒泡或不能冒泡到顶层,如果我们把IE的代码独立出来,标准浏览器用一套,IE用一套(旧版本IE,IE9在标准模式中支持dispatchEvent方法了),这样性能大大提高。zepto.js就是这么干的。

    在创建万能事件对象时,理应document.createEvent("Events");但早期浏览器需要用document.createEvent("HTMLEvents");

    万能事件理应能触发所有事件的回调,但是鼠标事件,要用MouseEvents参数创建鼠标对象事件才行。

    火狐不支持mousewheel,我们需要用document.createEvent("MouseScrollEvents")创建DOMMouseScroll事件对象。

    加油!

  • 相关阅读:
    Android sensor 系统框架 (一)
    enc28j60网卡驱动模块添加进linux内核,Kconfig,Makefile配置过程
    Linux samba服务器配置
    为群晖加把锁:使用ssh密钥保障数据安全
    浦发银行网上银行U盾证书无法更新的解决办法
    克隆Linux系统的网卡设置
    Linux路由:CentOS6的多种玩法
    专心学LINUX:CentOS关闭屏幕自动锁定和睡眠
    群晖:关闭软路由利用双网卡桥接直连电脑上网
    Ceph之二----部署Ceph集群
  • 原文地址:https://www.cnblogs.com/chaojidan/p/4177855.html
Copyright © 2011-2022 走看看