• 读jQuery之十一(添加事件核心方法)


    上一篇提到jQuery中添加事件提供给客户端程序员的接口方法有很多bind/click等,但其实现的核心方法是jQuery.event.add。这篇看看其源码,这个add定义如下(省略大部分)
    add: function( elem, types, handler, data ) {
    	if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
    		return;
    	}
    
               ...
    }
    

    定义了四个参数elem、types、handler和data分别为HTMLElement、事件类型(如click)、事件响应函数、数据。此外,types 可以以空格分开传多种事件("mouseover mouseout")。handler 有时会是一个对象(实现live时)。data 最后会挂在扩充后的event对象上,即作为event的属性。而event会在handler作为第一个参数拿到,这样也就可以在handler拿到data了。

     

    下面详细说明

    if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
    	return;
    }

    文本和注释节点直接返回。

     
    if ( handler === false ) {
    	handler = returnFalse;
    } else if ( !handler ) {
    	// Fixes bug #7229. Fix recommended by jdalton
    	return;
    }
    

    参数handler为false时,将handler赋值为returnFalse,returnFalse为一个函数,如下

    function returnFalse() {
    	return false;
    }
    

    jQuery通过handler为false来阻止元素默认行为,停止事件冒泡。这个需要结合jQuery.event.handle看。

     
    var handleObjIn, handleObj;
    
    if ( handler.handler ) {
    	handleObjIn = handler;
    	handler = handleObjIn.handler;
    }
    
    // Make sure that the function being executed has a unique ID
    if ( !handler.guid ) {
    	handler.guid = jQuery.guid++;
    }
    

    定义变量handleObjIn,handleObj。

    handler从字面上看是事件响应(回调)函数,但这里出现handler.handler,让人倍感怪异。即什么时候会将handler当一个JS对象传入呢?

    多数时候传的还是Function类型的,看看源码中jQuery.event.add的调用可发现jQuery在实现live的时候会传Object类型。如下

    add: function( handleObj ) {
    	jQuery.event.add( this,
    		liveConvert( handleObj.origType, handleObj.selector ),
    		jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
    },

    这时会把handleObjIn赋值为所传的JS对象,真正的handler 却是handleObjIn.handler。这话有点绕,慢慢体会。

     
    // Make sure that the function being executed has a unique ID
    if ( !handler.guid ) {
    	handler.guid = jQuery.guid++;
    }

    所传参数handler添加个属性guid,为一个数字,自增的从1开始。即使用jQuery添加事件,会为事件响应函数默认的添加了属性guid。这个guid再删除事件时会用到。

    // Init the element's event structure
    var elemData = jQuery._data( elem );
    

    先取elemData,这里使用了前面提到的jQuery._data。第一次为HTMLElement添加事件是elemData是个空对象({})。

     
    // If no elemData is found then we must be trying to bind to one of the
    // banned noData elements
    if ( !elemData ) {
    	return;
    }
    

    elemData不存在则直接返回。

     
    var events = elemData.events,
    	eventHandle = elemData.handle;

    定义events,eventHandle。同样第一次时这两个变量都是undefined。

     
    if ( !events ) {
    	elemData.events = events = {};
    }
    
    if ( !eventHandle ) {
    	elemData.handle = eventHandle = function( e ) {
    		// Discard the second event of a jQuery.event.trigger() and
    		// when an event is called after a page has unloaded
    		return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
    			jQuery.event.handle.apply( eventHandle.elem, arguments ) :
    			undefined;
    	
    	};
    }
    

    给elemData.events和elemData.handle赋值。

     
    // Add elem as a property of the handle function
    // This is to prevent a memory leak with non-native events in IE.
    eventHandle.elem = elem;
    

    暂存elem到eventHandle,删除事件注册时会将其置null,避免部分浏览器中内存泄露。

     
    // Handle multiple events separated by a space
    // jQuery(...).bind("mouseover mouseout", fn);
    types = types.split(" ");
    

    将字符串以空格为切割符转成数组。这句使其可以一次添加多个事件,多个事件的handler是相同的。

     

    后面是一个while循环

    while ( (type = types[ i++ ]) ) {
    	handleObj = handleObjIn ?
    		jQuery.extend({}, handleObjIn) :
    		{ handler: handler, data: data };
           
          ...
    
    }

    循环数组,里面依次处理如下

    1. 取得handleObj
    2. 处理事件命名空间,以点号(.)来区别。如果type有点号,则具有命名空间,否则没有
    3. 给handlerObj添加type,guid属性。这些后续删除事件时用到
    4. 取到handlers,special。多数情况下使用addEventListener/attachEvent来添加事件。从变量special可看出对于特殊的事件如ready,beforeunload及live事件是特殊处理的。  ready 调用的是jQuery.bindReady,而jQuery.bindReady内部调用的仍然是  addEventListener/attachEvent。beforeunload则是使用window.onbeforeunload来添加。live是实现事件代理的,他的处理也是特殊的
    5. 最后吧handleObj添加到数组handles中

    jQuery.event.add 的最后一句,解决IE中内存泄露。

    // Nullify elem to prevent memory leaks in IE
    elem = null;
    
     

    jQuery事件管理的数据结构,我做了个图。如下

     

  • 相关阅读:
    IO多路复用 IO异步
    你没听说过的协程
    事件驱动和IO操作
    堡垒机前戏——paramiko
    听说过的多进程,多线程到底是什么鬼
    socket套接字
    看见就烦的异常
    struts2值栈内部数据结构详解
    hibernate一级缓存的源码初窥
    使用自定义标签模拟jstl的<c:for each>标签
  • 原文地址:https://www.cnblogs.com/snandy/p/2102136.html
Copyright © 2020-2023  润新知