当前位置:首页 > Web开发 > 正文

jquery事件委托详解

2024-03-31 Web开发

标签:

jQuery事件委托处理流程

  上一章分析jQuery.event.add的时候已经分析了事件绑定,再把绑定的部分源码抽出来

if ( !(eventHandle = elemData.handle) ) { eventHandle = elemData.handle = function( e ) { //当一个事件被调用后页面已经卸载,,则放弃jQuery.event.trigger()的第二个事件, return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; //将elem作为handle函数的一个特征防止ie非本地事件引起的内存泄露 eventHandle.elem = elem; }
...
//非自定义事件,如果special事件处理器返回false,则只能使用addEventListener/attachEvent if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { //给元素绑定全局事件 if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, eventHandle ); } }

  绑定到elem上的事件处理是eventHandle,最终执行eventHandle的时候实际执行的是事件调度jQuery.event.dispatch。事件调度的的流程实际上就是处理委托事件的流程,因为本节点的响应处理最终会被附加到委托处理列表后面。

  事件调度流程为

  1. 从本地事件对象event构造一个可写的jQuery.Event对象。并用这个对象替换掉传参中的本地事件对象

//从本地事件对象构造一个可写的jQuery.Event event = jQuery.event.fix( event );
...
//使用修正过得jQuery.Event而不是(只读的)本地事件
args[0] = event;
event.delegateTarget = this;

  本地事件event的结构如下

  

技术图片

  使用本地事件构造的新事件对象jQuery.Event结构如下

  

技术图片

  其中originalEvent属性的值便是本地事件对象。构造的这个事件对象有很多属性都是直接从本地事件对象中抽出来的。

  2. 获取当前节点缓存中对应事件类型的事件处理列表

handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],

  事件处理列表的的顺序是委托事件处理在前面,最后才是直接绑定到当前节点的事件处理。

  3. 用当前节点替换jQuery.event.handlers的调用者并执行之,获取到符合要求的委托处理函数队列(这个队列最后会加上绑定到节点本身的处理事件)

//获取指定的事件处理队列,主要使用event.target事件源节点不断循环往上查找父节点, //看些节点和是否在handlers中的选择器对应的节点中 handlerQueue = jQuery.event.handlers.call( this, event, handlers );

  详细分析一下jQuery.event.handlers中获取符合要求的委托处理函数队列。

  jQuery.event.handlers先将委托事件处理取出来放在处理队列handlerQueue中。

  查找的过程是:先取出事件源cur = event.target;然后在确定有委托处理的情况下从事件源开始往他的祖先节点查询,直到this节点以下,遍历事件源到this节点(不包含this节点)的每个节点,在遍历中二次遍历委托事件列表中的每一个委托事件处理所指定的响应节点(委托事件处理对象的selector所指定)是否包含当前遍历节点(事件源到this节点中的当前节点)【handleObj.needsContext ?jQuery( sel, this ).index( cur ) >= 0 :jQuery.find( sel, this, null, [ cur ] ).length】,如果包含则往事件处理队列handlerQueue中压入该委托处理。源码如下

if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {   //冒泡父节点,找到匹配的委托事件存入handlerQueue队列   for ( ; cur != this; cur = cur.parentNode || this ) {     if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {       matches = [];       for ( i = 0; i < delegateCount; i++ ) {
        // 避免和Object.prototype属性冲突(#13203)
        sel = handleObj.selector + " ";
        if ( matches[ sel ] === undefined ) {
          matches[ sel ] = handleObj.needsContext ?
          jQuery( sel, this ).index( cur ) >= 0 :
          jQuery.find( sel, this, null, [ cur ] ).length;
        }
          
        if ( matches[ sel ] ) {
          matches.push( handleObj );
        }
      }
      //添加委托处理到队列中
      if ( matches.length ) {
        handlerQueue.push({ elem: cur, handlers: matches });
      }
    }
  }
}

  最后将直接绑定到当前节点的处理也压入执行

//添加直接绑定的事件到handlerQueue队列中 if ( delegateCount < handlers.length ) {   handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); }

  

  4. 执行事件处理队列handlerQueue中的处理函数

温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/web/40957.html