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

逻辑开拓者
• 阅读 2345

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

前言:
最重要的还是最后的流程图,可以试着根据流程图手写实现$().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')

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

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

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

可以看到

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之事件绑定到触发全过程及知识点补充


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

(完)

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这