客服:点击这里给我发消息

手机:15193566610 马先生

奈特网络科技,武威网站建设公司

您的位置:当前位置:武威网站-甘肃奈特网 > 网站建设 > 建站知识 >

Jquery源码分析---Event分析

来源:未知   发表时间:2015-11-10 10:10

    6.1 event的包裹

    浏览器的事件兼容性是一个令人头疼的问题。IE的 event在是在全局的window下,而mozilla的event是事件源参数传入到回调函数 中。还有很多的事件处理方式也一样。

    Jquery提供了一个 event的包裹 ,这个相对于其它的lib提供的有点简单,但是足够使用。

    //对事 件进行包裹。
      fix : function(event) {
        if (event [expando] == true) return event;//表明事件已经包裹过
        //保 存原始event,同时clone一个。
        var originalEvent = event;                 ①
        event = { originalEvent : originalEvent};
        for (var i = this.props.length, prop;i;) {
          prop = this.props[--i];
          event[prop] = originalEvent[prop];
        }
        event[expando] = true;   
        //加上preventDefault and stopPropagation,在 clone不会运行
        event.preventDefault = function() {           ②
          // 在原始事件上运行
          if (originalEvent.preventDefault)
            originalEvent.preventDefault();
          originalEvent.returnValue = false;
        };
        event.stopPropagation = function() {
          // 在原始事件上运 行
          if (originalEvent.stopPropagation)
            originalEvent.stopPropagation();
          originalEvent.cancelBubble = true;
        };
        // 修正 timeStamp
        event.timeStamp = event.timeStamp || now();
        // 修正target
        if (!event.target)                     ③
          event.target = event.srcElement || document;    
        if (event.target.nodeType == 3)//文本节点是父节点。
          event.target = event.target.parentNode;
        // relatedTarget
        if (!event.relatedTarget && event.fromElement)   ④
          event.relatedTarget = event.fromElement == event.target
             ? event.toElement : event.fromElement;
        // Calculate pageX/Y if missing and clientX/Y available
        if (event.pageX == null && event.clientX != null) { ⑥
          var doc = document.documentElement, body = document.body;
         event.pageX = event.clientX
           + (doc && doc.scrollLeft || body && body.scrollLeft || 0)
              - (doc.clientLeft || 0);
          event.pageY = event.clientY
           + (doc && doc.scrollTop || body && body.scrollTop || 0)
             - (doc.clientTop || 0);
        }

        // Add which for key events
      if (!event.which && ((event.charCode || event.charCode === 0) ⑦
               ? event.charCode : event.keyCode))
          event.which = event.charCode || event.keyCode;

      // Add metaKey to non-Mac browsers
        if (!event.metaKey && event.ctrlKey)             ⑧
          event.metaKey = event.ctrlKey;
      // Add which for click: 1 == left; 2 == middle; 3 == right
      // Note: button is not normalized, so don't use it
        if (! event.which && event.button)             ⑨
          event.which = (event.button & 1 ? 1 : (event.button & 2
             ? 3 : (event.button & 4 ? 2 : 0)));
        return event;
    },

     

     

     

    上面的代码①处保 留原始事件的引用,同时clone原始事件。在这个clone的事件上进行包裹。②处 在原始事件上运行preventDefault 和 stopPropagation两个方法达到是否阻止 默认的事件动作发生和是否停止冒泡事件事件向上传递。

    ③处是修正 target个,IE中采用srcElement,同时对于文本节点事件,应该把target传到其 父节点。

    ④处relatedTarget只是对于mouseout、mouseover有用。在IE 中分成了to和from两个Target变量,在mozilla中没有分开。为了保证兼容,采 用relatedTarget统一起来。

    ⑥处是进行event的坐标位置。这个是相对 于page。如果页面可以scroll,则要在其client上加上scroll。在IE中还应该减 去默认的2px的body的边框。

    ⑦处是把键盘事件的按键统一到 event.which的属性上。Ext中的实现ev.charCode || ev.keyCode || 0; ⑨则是 把鼠标事件的按键统一把event.which上。charCode、ev.keyCode一个是字符的 按键,一个不是字符的按键。⑨处采用&的方式来进行兼容性的处理。 Ext 通过下面三行解决兼容问题。

     var btnMap = Ext.isIE ? {1:0,4:1,2:2} : (Ext.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2}); this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : - 1);

    ①②③④⑤⑥⑦⑧⑨⑩

    6.2 事件的处理

    Jquery提供了一些来进行regist,remove,fire事件的方法。

    6.2.1 Register

    对于注册事件,jquery提供了bind、one、 toggle、hover四种注册事件的方法, bind是最基本的方法。One是注册只运行 一次的方法,toggle注册交替运行的方法。Hover是注册鼠标浮过的方法。

    bind : function(type, data, fn) {
      return type == "unload" ? this.one(type, data, fn) : this
        .each(function() {// fn || data, fn && data实现了data参数可有 可无
             jQuery.event.add(this, type, fn || data, fn && data);
           }); },

    Bind中对于 unload的事件,只能运行一次,其它的就采用默认的注册方式。

    // 为每一个匹配元素的特定事件(像click)绑定一个一次性的 事件处理函数。
    // 在每个对象上,这个事件处理函数只会被执行一次。 其他规则与bind()函数相同。
    // 这个事件处理函数会接收到一个事件对 象,可以通过它来阻止(浏览器)默认的行为。
    // 如果既想取消默认的 行为,又想阻止事件起泡,这个事件处理函数必须返回false。
      one : function(type, data, fn) {
       var one = jQuery.event.proxy(fn || data, function(event) {
          jQuery(this).unbind(event, one);
          return (fn || data).apply(this, arguments);/this->当前的元素
           });
          return this.each(function() {
           jQuery.event.add (this, type, one, fn && data);
          });
       },

    One与bind基本上差不多,不同的在调用jQuery.event.add时 ,把注册的事件处理的函数做了一个小小的调整。One调用了 jQuery.event.proxy进行了代理传入的事件处理函数。在事件触发调用这个代理 的函数时,先把事件从cache中删除,再执行注册的事件函数。这里就是闭包的 应用,通过闭包得到fn注册的事件函数的引用。

    //一个模仿悬停 事件(鼠标移动到一个对象上面及移出这个对象)的方法。
    //这是一个自 定义的方法,它为频繁使用的任务提供了一种“保持在其中”的状态 。
    //当鼠标移动到一个匹配的元素上面时,会触发指定的第一个函数。当 鼠标移出这个元素时,
    /会触发指定的第二个函数。而且,会伴随着对鼠 标是否仍然处在特定元素中的检测(例如,处在div中的图像),
     //如 果是,则会继续保持“悬停”状态,而不触发移出事件(修正了使用 mouseout事件的一个常见错误)。
        hover : function(fnOver, fnOut) {
          return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
       },

     

     

     

    Hover则是建立在bind的基础之上。

    //每次点击 后依次调用函数。
    toggle : function(fn) {  
    var args = arguments, i = 1;
    while (i < args.length)//每个函数分配GUID
        jQuery.event.proxy(fn, args[i++]);//修改后的还在args中
    return this.click(jQuery.event.proxy(fn, function(event) {//分配GUID      this.lastToggle = (this.lastToggle || 0) % i;//上一个函数       event.preventDefault();//阻止缺省动作
        //执行参数中 的第几个函数,apply可以采用array-like的参数
        return args [this.lastToggle++].apply(this,arguments)||false;
      }));
      },

    Toggle中参数可以是多个fn。先把它们代码生成UUID。之 后调用click的方法来注册再次进行代理的callback。这个函数在事件触发时运 行,它先计算上一次是执行了参数中的那个函数。之后阻止缺省动作。之后找到 下一个函数运行。

    //为jquery对象增加常用的事件方法
    jQuery.each(
       ("blur,focus,load,resize,scroll,unload,click,dblclick,"
      + "mousedown,mouseup,mousemove,mouseover,mouseout,change,select,&qu ot;
    + "submit,keydown,keypress,keyup,error").split (","),
    function(i, name) {jQuery.fn[name] = function (fn) {
             return fn ? this.bind(name, fn) : this.trigger(name);
           };});

    Jquery增加了 一个常用的事件处理方法,包含上面调用的click。这里可以看出这里还是调用 bind进行注册。当然这里还可以通过程序实现去触发事件。

    上面的众多 方法都是注册事件,其最终都落在jQuery.event.add();来完成注册的功能。如 果我们采用Dom0或DOM1的事件方法,我们会采用elem.onclick=function(){}来 为元素的某一种事件来注册处理函数。这个最大的缺点就是每个一个事件只是一 个处理函数。在dom1的方式中有改进,我们可以采用elem.addEventListener (type, handle, false)为元素的事件注册多个处理函数。

    这样的处理方 式还不是很完美,如果我们只这个事件运行一次就有点麻烦了。我们要在事件的 处理函数中最后进行elem.removeEventListener来取消事件的监听。这样做可能 会有事务上的问题。如果第一个事件处理函数在没有取消事件监听之前,就再次 触发了怎么办?

    还有采用浏览器的方式,它不支持自定义事件的注册和 处理,还不能为多个事件注册同一个处理函数。

    jQuery.event = {// add 事件到一个元素上。
    add : function(elem, types, handler, data) {
    if (elem.nodeType == 3 || elem.nodeType == 8) return;// 空白节点或注释
      // IE不能传入window,先复制一下。
    if (jQuery.browser.msie && elem.setInterval) elem = window;
    // 为handler分配一个全局唯一的Id
    if (!handler.guid)  handler.guid = this.guid++;
    // 把data附到handler.data中
    if (data != undefined) {                       ① 
    var fn = handler;
    handler =this.proxy(fn,function(){return fn.apply(this,arguments);});
    handler.data = data;
      }
    // 初始化元素的events。如果没有取到events中值,就初始化data: {}     ②
    var events =jQuery.data(elem,"events") ||jQuery.data(elem,"events",{}),
    // 如果没有取到handle 中值,就初始化data: function() {....}     ③
    handle = jQuery.data(elem, "handle")|| jQuery.data(elem, "handle",
    function() {//处理一个触发器的第二个事件和当 page已经unload之后调用一个事件。
        if (typeof jQuery != "undefined"&& !jQuery.event.triggered)
           return jQuery.event.handle.apply(//callee.elem=handle.elem
              arguments.callee.elem, arguments);
          });
    // 增加elem做为handle属性,防止IE由于没有本地Event而内存泄露 。
    handle.elem = elem;
    // 处理采用空格分隔多个事件名,如 jQuery(...).bind("mouseover mouseout", fn);
    jQuery.each(types.split(/s+/), function(index, type) {    ④
      // 命名空间的事件,一般不会用到。
    var parts = type.split (".");type = parts[0];handler.type = parts[1];
      // 捆绑到本元素type事件的所有处理函数
    var handlers = events[type];                      ⑤
    if (!handlers) {// 没有 找到处理函数列表就初始化事件队列
        handlers = events[type] = {};
      // 如果type不是ready,或ready的setup执行返回false          ⑥
    if (!jQuery.event.special[type]|| jQuery.event.special[type].setup
        .call(elem, data) === false) {// 调用系统的事件函数来注册事件
    if (elem.addEventListener)elem.addEventListener(type,handle,false);
    else if (elem.attachEvent)elem.attachEvent("on" + type, handle);
         }
    }
    // 把处理器的id和handler形式属性 对的形式保存在handlers列表中,
    // 也存在events[type] [handler.guid]中。
    handlers[handler.guid] = handler;                   ⑦
    // 全局缓存这个事件的使用标识
    jQuery.event.global[type] = true;
    });

      elem = null; // 防止IE内存泄露。
      },
      guid : 1,
       global : {},

     

     

     

    jQuery.event.add通过jQuery.data把事件相关的 事件名和处理函数有机有序地组合起存放在jQuery.cache中与该元素对应的空间 里。我们就一个例子分析一下add的过程中:假如我们招待下面jQuery (e1).bind("mouseover mouseout", fn0);jQuery(e1).bind ("mouseover mouseout", fn1)的语句。

    在jQuery(e1).bind ("mouseover mouseout", fn0);时,②③都不可能从cache取到数, 先初始化。此时的cache:{e1_uuid:{events:{},handle:fn}}。接着在⑤会为 mouseover mouseout名初始化。此时的cache: {e1_uuid:{events:{ mouseover:{}, mouseout:{}},handle:fn}}。在⑥处向浏览器的事件中注册处理 函数。接着⑦会把处理函数到事件名中。此时的cache: {e1_uuid:{events: {mouseover:{fn0_uuid:fn0},mouseout:{ fn0_uuid:fn0}},handle:fn}}。这里 可以看出为采用proxy为函数生成uuid的作用了。

    在jQuery(e1).bind ("mouseover mouseout", fn1)时,②③都从cache取到数据 {e1_uuid:{events:{mouseover:{fn0_uuid:fn0},mouseout:{ fn0_uuid:fn0}}, 接着在⑤取到mouseover:{fn0_uuid:fn0},mouseout:{ fn0_uuid:fn0}的引用。 接着⑦会把处理函数注册到事件名中。此时的cache: {e1_uuid:{events: {mouseover:{fn0_uuid:fn0, fn1_uuid:fn1,},mouseout:{ fn0_uuid:fn0, fn1_uuid:fn1}},handle:fn}}。

    jQuery.event.add很重要的任务就是把 注册的事件函数有序地存放起来。以便remove和fire事件的函数能找到。

    //{elem_uuid_1:{events:{mouseover: {fn_uuid:fn1,fn_uuid1:fn2},
          //mouseout: {fn_uuid:fn1,fn_uuid1:fn2}},handle:fn}}

    6.2.2 trigger

    注册了事件,如onclick。那么当用户点击这个元素时,就会自 动触发这个事件的已经注册的事件处理函数。但是我们有的时候要采用程序来模 拟事件的触发就得采用强迫触发某个事件。在IE中我们可以采用.fireEvent()来 实现。如:<form onsubmit="a()" >中,如果button的 form.submit()的方式提交表单,是不会主动触发onsumbit事件的,如果必须的 话,就要在submit前$(“:form”)[0].fireEvent ("onsubmit”,),这样就会触发该事件。

    在mozilla中有三个 步骤:  var  evt  =  document.createEvent ('HTMLEvents');

    evt.initEvent ('change',true,true);  t.dispatchEvent( evt );

    在 prototype是采用这样的方式来实现的。那么jquery中呢,它的实现方式有一点 不一样。

    trigger : function(type, data, fn) {
    return this.each(function() {
        jQuery.event.trigger(type, data, this, true, fn);
          }); },

    Trigger有三个参数 ,data参数是为了注册的事件函数提供了实传。如果data[0]中preventDefault 存在,data[0]就可以做为用户自定义的包裹事件的空间。Fn是可以为事件提供 一个即时即用的事件处理方法。也就是在没有注册事件的情况下也可以通过传入 处理函数来处理事件。如果已经注册了,那就是在原来的事件处理函数之后执行 。

      //这个方法将会触发指定的事件类型上所有绑定的处理函 数。但不会执行浏览器默认动作.
    triggerHandler : function(type, data, fn) {
    return this[0]&& jQuery.event.trigger (type,data,this[0],false,fn);
      },

    triggerHandle通 过把jQuery.event.trigger的donative参数设为false,来阻止执行浏览器默处 理方法。它与trigger不现的一点,还在于它只是处理jquery对象的第一个元素 。

    上面两个方法都调用了jQuery.event.trigger来完成任务:

    trigger : function(type, data, elem, donative, extra) {
      data = jQuery.makeArray(data);//data可以为{xx:yy}
       //支持getData!这样的形式,exclusive = true表现会对add的注册的
       //事件的所有函数进行命名空间的分种类的来执行。
    if (type.indexOf("!") >= 0) {             ①
        type = type.slice(0, -1);var exclusive = true;
        } 
    if (!elem) {// 处理全局的fire事件            ②
      if (this.global[type])
        jQuery.each(jQuery.cache, function() {
         // 从cache中找到所有注册该事件的元素,触发 改事件的处理函数
          if (this.events && this.events[type])
           jQuery.event.trigger(type, data, this.handle.elem);
          });
      } else {// 处理单个元 素事件的fire事件          ③
       if (elem.nodeType == 3 || elem.nodeType == 8)  return undefined;
       var val, ret, fn = jQuery.isFunction(elem[type] || null),
       // 如果data参数 传进入的不是浏览器的event对象的话,event变量为true.
      //如果 data参数本身是娄组,那么第一个元素不是浏览器的event对象时为true.
      //对于event为true。即没有event传进入,先构建一个伪造的event对象存 在data[0]。
      event = !data[0] || !data[0].preventDefault;
      // 在没有传入event对象的情况下,构建伪造event对象。
      if (event) {//存到数组中的第一个             ④
        data.unshift( { type : type,target : elem,
           preventDefault : function() {},stopPropagation :
    function() {}, timeStamp : now()  });
        data[0][expando] = true; // 不需要 修正伪造的event对象
        }
       data[0].type = type; //防 止事件名出错
      //表现会进行事件注册函数的分类(命名空间)执行。 不是所有的。
       if (exclusive) data[0].exclusive = true;

      //与prototype等传统的处理方式不一样,没有采用fireEvent来 
      //来fire通过注册到浏览器事件中的事件处理方法。
      //这 里分了三步,先fire通过jQuery.event.add来注册的事件,这个事件
       //有可能是自定义的事件(没有注册到浏览器事件中)。
      //第二步是 fire通过elem.onclick方式注册的事件的本地处理函数
       //第三步是 fire默认的事件处理方式(在本地的onclick的方式注册
       //不存在的 情况下)。  
    // 这里是触发通过jQuery.event.add来注册的事件,
       var handle = jQuery.data(elem, "handle");       ⑤
       if (handle)val = handle.apply(elem, data); //这里data 分成多个参数
      //处理触发通过elem.onfoo=function()这样的注册本 地处理方法,
      //但是是对于links 's .click()不触发,这个不会 执行通过addEvent
      //方式注册的事件处理方式。     
       if ((!fn || (jQuery.nodeName(elem, 'a') && type == "click")) ⑥
        && elem["on"+ type]&& elem["on"+type].apply(elem,data) === false) 
       val = false;
    //额外的函数参数的开始几个是通过data给定 的。这里会把伪造加上的event给去掉。
    //它的最后一个参数是一系列的 事件处理函数返回的结果,一般为bool值
    //这个函数可以根据这个结果来 处理一个扫尾的工作。
      if (event) data.shift();
    // 处理触 发extra给定的函数处理。
      if (extra && jQuery.isFunction(extra)) {           ⑦
        ret = extra.apply(elem, val == null ? data : data.concat(val));
         //如果这个函数有返回值,那么trigger的返回值就是它的返回值
         //没有的话就是串连的事件处理函数的最后一个返回值。一般为bool
        if (ret !== undefined)  val = ret;
       }
      // 触 发默认本地事件方法,它是在没有如.onclick注册事件
      //加上前面的 执行事件处理函数返回值都不为false的情况下,才会执行。
      //它还 可以通donative来控制是否执行。
      //如form中可以采用this.submit ()来提交form.
      if (fn && donative !== false && val !== false     ⑧
           && ! (jQuery.nodeName(elem, 'a') && type == "click")) {
        this.triggered = true;
        try {elem[type]();  //对于一些hidden的元素,IE会报错
          } catch (e) {}
        }
      this.triggered = false;
       }
    return val;
    },

     

     

     

    Jquery的fire事件的方法与 prototype中实现是完全不一样的。Ext、YUI没有提供强迫触发事件的方法。对 于一般的思维,程序来触发浏览器的事件就应该采用fireEvent或dispatchEvent 方法来运行。

    但是jquery采用一种不同的方法。对于通过 jquery.event.add来注册的事件(不管是自定义的还是注册到浏览器事件),它 保存在一个与元素及事件名相对应的cache中。在浏览器的触发中,这个是没有 什么作用。但是它是为了通过等程序来强迫触发时,从cache中取到对应的事件 处理函数。这个时候就抛开了浏览器的事件。在这里还可以执行一些自定义的事 件函数。如⑤处。

    对于通过html的标签中如click或 elem.onclick=function(){}形式注册的事件函数。在⑥处它采用执行元素的如 onclick形式的回调函数就可以。通过这种dom0的方式只能注册一个函数。

    有的时候,如果没有onclick这样的事件处理函数,浏览器会执行默认的 处理函数。如form.submit()。⑧处可以看出对于这样的默认的事件处理,还可 以通过参数donative来控制。

    程序手动强迫触发事件,有一点问题就是 event是怎么生成,就是没有浏览器生成event传入到函数中。Prototype采用了 是新生成的dataavailable的事件。这样的事件也没有什么作用。Jquery也采用 fake的方式伪造一个一个事件,如④,它比prototype的事件好处在于它能通过 trigger的函数的参数来传入需要的event。Prototype则不能。

    通过上面 的分析,隐隐可以看出Jquery是通过模拟浏览器的触发事件的执行过程来构建这 个trigger的函数的。先执行dom1方式(addEvent)注册的事件,再执行dom0方 式注册的事件,最后看看要不要执行默认的事件处理。

    在⑦处,我们可 以看出trigger还可能通过传入回调函数和参数来完成对执行的事件处理函数的 结果进行判断处理,形成新结果通过trigger的函数返回。这在有的时候是很有 用的。

    除了这些,它还能对于事件的处理函数进行分类(namespace), 可以在合适的时候调用事件的不同分类的的处理函数(通过jquery.event.add来 注册)。这个分类的处理在handle实现。

      handle : function(event) {
        // 返回 undefined or false
        var val, ret, namespace, all, handlers;
        //修改了传入的参数 ,这里是引用。
        event = arguments[0] = jQuery.event.fix (event || window.event);
        // 命名空间处理
        namespace = event.type.split(".");
        event.type = namespace[0];
        namespace = namespace[1];
        // all = true 表明任何 handler,namespace不存在,同时
        //event.exclusive不存在或为假时,all=true.
        all = ! namespace && !event.exclusive;
        // 找到元素的events 中缓存的事件名的处理函数列表
        handlers = (jQuery.data(this, "events") || {})[event.type];
        for (var j in handlers) {// 每个处理函数执行
          var handler = handlers [j];
          // Filter the functions by class
          if (all || handler.type == namespace) {
           // 传入引用 ,为了之后删除它们
           event.handler = handler;
            event.data = handler.data;//add的时候加上的
            ret = handler.apply(this, arguments);// 执行事件处理函数
             if (val !== false)
             val = ret;// 只 要有一个处理函数返回false,本函数就返回false.
           if (ret === false) {// 不执行浏览器默认的动作
              event.preventDefault();
             event.stopPropagation ();
           }
          }
        }
        return val;  },

    handle的主要功能是就是分类且有序地执行事 件的所有的注册的处理函数。

     

     

     

    6.2.3 remove

    Remove就是除去事件 的监听,在这里的实现,它还要除去cache中对应的的数据。

    // 从元素中remove一个事件
    remove : function(elem, types, handler) {
      if (elem.nodeType == 3 || elem.nodeType == 8) return;
      // 取出元素的events中Fn列表
      var events = jQuery.data (elem, "events"), ret, index;
      if (events) {     
          // remove所有的该元素的事件 .是命名空间的处理
         if (types == undefined
             || (typeof types == "string" && types.charAt(0) == "."))
          for (var type in events)
              this.remove(elem, type + (types || ""));//xx.yy
        else {
           // types, handler参数采用 {type:xxx,handler:yyy}形式
          if (types.type) {
              handler = types.handler;
             types = types.type;
           }
    // 处理采用空格分隔多个事件名 jQuery(...).unbind("mouseover mouseout", fn);
       jQuery.each(types.split(/s+/), function(index, type) {
              // 命名空间的事件,一般不会用到。
        var parts = type.split(".");
        type = parts[0];
        if (events[type]) {// 事件名找到
         if (handler)// handler传入 ,就remove事件名的这个处理函数
          delete events[type] [handler.guid];//guid的作用
         else // remove这个事件的所 有处理函数,带有命名空间的处理
          for (handler in events[type])
           if (!parts[1]|| events[type] [handler].type == parts[1])
             delete events [type][handler];
        // 如果没有该事件的处理函数存在,就remove 事件名
          for (ret in events[type])  break; // 看看有没 有?
           if (!ret) {// 没有
           if (! jQuery.event.special[type] || jQuery.event.special
    [type].teardown.call(elem) === false) {//type不等于ready
              if (elem.removeEventListener)// 在浏览器中remove事件名         elem.removeEventListener(type,jQuery.data(elem,
                       handle"), false);
              else if (elem.detachEvent)
               elem.detachEvent("on" + type,jQuery.data(elem,
                         "handle"));
            }
          ret = null;
          delete events[type];// 在缓存中除去。
           }
          }
      });
    }

      // 不再使用,除去expando
      for (ret in events)   break;
        if (!ret) {
           var handle = jQuery.data(elem, "handle");
           if (handle)
             handle.elem = null;
            jQuery.removeData(elem, "events");
            jQuery.removeData(elem, "handle");
          }
         }
      },

    ①②③④⑤⑥⑥⑦⑧⑨

     

     

     

    6.3 domReady的处理

    Domready是每个lib都要实现的函数,因为dom还没有完 全ready,那么对于对于dom元素的操作可能出错,因为dom树中可能还没有Load 这个元素。为了保障不出现这样的错误,就出现地domready。对于每种浏览器 Domready都有着自己不同的判断。

    Jquery的domready的实现和其它的lib 的实现没有什么区别。

      //dom ready时执行 fn
       ready : function(fn) {     
          bindReady();//注册监 听    
          if (jQuery.isReady)//ready就运行      
           fn.call(document, jQuery);    
          else
           // 增加这个函数到queue中。可见支持无数的ready 的调用。
           jQuery.readyList.push(function() {
              return fn.call(this, jQuery);
            });
          return this;
       }

    我们通过 $(fn)的方法就是在调用这个方法。它首先通过bindReady()来注册监听dom是否 已经loaded。如果已经loaded就运行fn。如果没有就把fn存放在readyList中。 对于多个$(fn),把它们各自的fn参数保存在公共的jQuery. readyList的公共集 合中。待到dom loaed之后统一运行。对于dom 已经loaded,就可以分别去运行 fn。

    var readyBound = false;

    function bindReady() {
      if (readyBound)
        return;
       readyBound = true;

      // Mozilla, Opera, webkit nightlies 支持DOMContentLoaded事件
    if (document.addEventListener && !jQuery.browser.opera)
        //当DOMContentLoaded事件触发时就运 行jQuery.ready
    document.addEventListener ("DOMContentLoaded", jQuery.ready, false);

       //IE或不是frame的window
      if (jQuery.browser.msie && window == top)
        (function() {
          if (jQuery.isReady)
           return;
          try {
           // 在ondocumentready之前,一直都会抛出异常       
           // http://javascript.nwbox.com/IEContentLoaded/
           document.documentElement.doScroll ("left");
          } catch (error) {
            //一直运行bindReady()(=arguments.callee)
            setTimeout(arguments.callee, 0);
           return;
           }
          jQuery.ready();//documentready就运行 jQuery.ready
        })();

      if (jQuery.browser.opera)
        document.addEventListener ("DOMContentLoaded", function() {
          if (jQuery.isReady)
           return;
           //只有 styleSheets完全enable时,才是完全的load,其实还有pic
        for (var i = 0;i < document.styleSheets.length; i++)
         if (document.styleSheets[i].disabled) {//通过styleSheets来判断
              setTimeout(arguments.callee, 0);
              return;
           }
           jQuery.ready();
          }, false);

      if (jQuery.browser.safari) {
        var numStyles;
        (function() {
          if (jQuery.isReady)
           return;
           //首先 得得到readyState=loaded或=complete
          if (document.readyState != "loaded"
              && document.readyState != "complete") {
            setTimeout(arguments.callee, 0);
           return;
          }
          //取得style的length,比较它们之间的长度 ,看看是不是完成loaded
          if (numStyles === undefined)
           numStyles = jQuery("style,
    link [rel=stylesheet]").length;
          if (document.styleSheets.length != numStyles) {
            setTimeout(arguments.callee, 0);
           return;
           }
          jQuery.ready();
        })();
      } 

      //最后只能依赖于window.load.
       jQuery.event.add(window, "load", jQuery.ready);
    }

    这段代码看起来很多,其实就是采用setTimeout (arguments.callee, 0);反复来运行bindReady。如果其得到dom ready的条件满 足的话,就执行jQuery.ready()来执行通过$(fn)注册的fn函数。对于每种浏览 器,这个满足的条件是不一样。上面的代码就是针对于几种常用的浏览器分别做 了各自的处理。

    isReady : false,
      readyList : [],
      // Handle when the DOM is ready
        ready : function() {    
          if (!jQuery.isReady) {   
           jQuery.isReady = true;      
            if (jQuery.readyList) {        
              jQuery.each(jQuery.readyList, function() {
               this.call(document);
             });     
              jQuery.readyList = null;
           }
            jQuery(document).triggerHandler("ready");
           }
        }
     });

    当运行到jQuery.ready() 的时候就说明dom已经完全的Loaded,那么现在就应该执行保存在 jQuery.readyList中的fn。jQuery.ready()就是完成这个工作。

    文章来 源:http://jljlpch.javaeye.com/category/37744