美文网首页
jQuery之事件绑定到触发全过程及知识点补充

jQuery之事件绑定到触发全过程及知识点补充

作者: 小进进不将就 | 来源:发表于2019-06-17 19:55 被阅读0次

    前言:
    最重要的还是最后的流程图,可以试着根据流程图手写实现$().on(),下篇文章会放出模拟实现的代码。

    一、举例

    <div id="A" style="background-color: deeppink">
      这是A
    
        <div id="C" style="background-color: aqua">
        这是C
        </div>
    
    </div>
    
      $("#A").on("click" ,function (event) {
        console.log(event,"A被点击了")
      })
    
      $("#A").on("click" ,"#C",function (event) {
        console.log(event,"点击了C,即C委托A的click事件被点击了")
      })
    

    二、$().on()
    (1)进行参数的调整
    (2)调用jQuery.event.add()方法

    三、jQuery.event.add()最终调用elem.addEventListener()来绑定事件
    注意:
    (1)绑定常用的事件(如:click、focus),使用handleObj保存

    handleObj = jQuery.extend( {
              //click,mouseout...
              type: type,
              //click,mouseout...
              origType: origType,
              data: data,
              //事件处理函数,如 function(){console.log('aaaa')}
              handler: handler,
              //索引,用于关联元素和事件
              guid: handler.guid,
              //事件委托的标志,也是委托的对象选择器
              selector: selector,
              needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
              //命名空间,同一click事件有两个事件处理程序handler的话,
              //用这个标识,方便删除或添加handler
              namespace: namespaces.join( "." )
            }, handleObjIn );
    

    (2)如果绑定的是自定义事件(如:windowResize),则使用handleObjIn保存

    if ( handler.handler ) {
       handleObjIn = handler;
       handler = handleObjIn.handler;
       selector = handleObjIn.selector;
    }
    

    (1)、(2)都会初始化事件处理器(addEventListener):

            //第一次绑定事件,走这里
            // Init the event handler queue if we're the first
            if ( !( handlers = events[ type ] ) ) {
              handlers = events[ type ] = [];
              handlers.delegateCount = 0;
              // Only use addEventListener if the special events handler returns false
              if ( !special.setup ||
                special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
                //目标元素有addEventListener的话,调用绑定click事件
                //eventHandle就绑定到addEventListener上
                if ( elem.addEventListener ) {
                  elem.addEventListener( type, eventHandle );
                }
              }
            }
    

    四、jQuery的事件绑定为何不直接绑定在目标元素身上,而是元素和事件分离?
    打印$("#A")

    console.log($("#A"),'aaaaaa46')
    

    不要在意jQueryId不同的问题,每次刷新网页它都会变化

    可以看到

    jQuery的事件和触发事件的handler是分离的,
    事件集合 存在 事件缓存dataPrivevents上,

    //获取数据缓存
    elemData = dataPriv.get( elem );
    

    而handler是由jQuery.event.dispatch()处理

    elemData.handle = function( e ) {
      jQuery.event.dispatch.apply( elem, arguments )
    }
    

    为什么要分离?因为元素如果绑定click事件一百次,很耗内存。所以需要将这一百个同类型的事件保存到一个click事件集合中,然后在这一大个click事件集合内,根据guid来执行某一次的click处理代码

    同一事件的处理:

    $('body').on('click', '#one', function(e) {
      show('委托到one触发')
    })
    $('body').on('click', '#two', function(e) {
      show('委托到two触发')
    })
    

    events是jQuery内部的事件队列
    handle是真正绑定到element上的事件处理函数

    body:{
      events:{
        click:[
          0:{
            guid: 1,
            data: undefined,
            namespace: "",
            origType: "click",
            //事件委托的标志
            selector: "#one",
            type: "click",
            handler: function(){xxx},
            }
          1:{
            guid: 2,
            data: undefined,
            namespace: "",
            origType: "click",
            //事件委托的标志
            selector: "#two",
            type: "click",
            handler: function(){xxx},
            }
        ]
      },
      handle: function(){
        jQuery.event.dispatch.apply( elem, arguments )
      }
    }
    

    可以看到,针对同一类型的事件(如click),重复绑定不会再创建新的内存(new Object会有新内存),而是在events里添加新的绑定事件。

    记得看第十一点!

    五、guid的作用?
    添加guid的目的是因为handler没有直接跟元素节点发生关联,所以需要一个索引来寻找或者删除handler

    六、命名空间namespace的作用?

     $("#one").on("click.one",function () {
       console.log("one被点击了")
     })
     $("#one").on("click.two",function () {
       console.log("two被点击了")
     })
    

    命名空间为:

    #one:{
      events:{
        click:[
          0:{
            namespace: "one",
            handler: function(){console.log("one被点击了")},
            }
          1:{
            namespace: "two",
            handler: function(){xxx},
            }
        ]
      },
    }
    

    利用命名空间删除事件:

    $("#one").off("click.one")
    

    七、jQuery.event.special 的处理机制
    绑定的事件,有些是不能统一处理的,比如load事件,是不支持冒泡的,所以即使开发者未用event.stopPropagation,jQuery也要阻止其冒泡:

    jQuery.event ={
      special: {
        load: {
            // Prevent triggered image.load events from bubbling to window.load
            //阻止冒泡
            noBubble: true
        },
        focus: {
            // Fire native event if possible so blur/focus sequence is correct
            trigger: function() { },
            delegateType: "focusin"
        },
      }
    }
    

    八、外部是Event,内部是数据缓存events,两者是不一样的
    外部Event:

    $().on("click","#B",function(event){
      console.log("A被点击了")
    })
    
    //click的event就是jQuery.Event
    jQuery.Event{
      handleObj{
        data:undefined,
        guid: 2,
        handler:function(){console.log("A被点击了")},
        namespace: "clickA",
        origType: "click",
        selector: "#B",
        type: "click.clickA",
      },
      originalEvent:{
        //就是MouseEvent
      },
      target:div#B,
      type: "click",
      delegateTarget: div#A,
      //fix 的标志
      jQuery331087940272164138: true,
      currentTarget: div#A,
      isDefaultPrevented:xxx,
      timeStamp:Date.now(),
      isDefaultPrevented:function(){return false}
    }
    

    内部缓存events:

    let events = dataPriv.get( this, "events" )
    
    events[
        delegantCount:1,
       {
          data:undefined,
          guid: 2,
          handler:function(){console.log("B委托A被点击了")},
          namespace: "clickA",
          origType: "click",
          selector: "#B",
          type: "click.clickA",
        },
       {
         data:undefined,
          guid: 1,
          handler:function(){console.log("A被点击了")},
          namespace: "",
          origType: "click",
          selector: undefined,
          type: "click",
        }   
    
    
    ]
    
    

    九、为什么要使用fix()来重构 js 的原生 MouseEvent 对象呢?
    (1)jQuery 有自己的一套event处理机制,所以需要符合jQueryevent对象
    (2)可以传递 data 数据,即用户自定义的数据。

    十、trigger的触发机制

      $("#A").on("click" ,function (event) {
        console.log(event,"A被点击了")
      })
    

    元素#A本身绑定了一个click事件,但是click是原生事件,它是靠 addEventListener绑定来触发事件的。

    但是!jQuerytrigger是能够无差别模拟这个交互行为的

    $("#A").trigger("click")
    

    trigger()的功能上就可以解释 为什么jQuery要设计元素与数据分离了:

    如果是直接绑定的话就无法通过trigger的机制去触发click事件,
    正是因为jQuery没有直接把事件相关的handler与元素直接绑定,而是采用了分离处理,
    所以我们通过trigger触发click事件与addEventListener触发click事件的处理流程是一致的,不同的只是触发的方式而已。

    但是,通trigger触发的事件是没有事件对象(event)、冒泡(bubble)这些特性的,所以我们需要有一个功能 能模拟出事件对象,然后生成一个遍历树(eventPath)模拟出冒泡行为,这个就交给了trigger方法了

    关于$().trigger()的源码解析请看:jQuery源码解析之trigger()


    最后,附上自己做的 jQuery事件绑定到触发全过程的流程图:


    (完)

    相关文章

      网友评论

          本文标题:jQuery之事件绑定到触发全过程及知识点补充

          本文链接:https://www.haomeiwen.com/subject/mdufxctx.html