前言:
最重要的仍是最后的流程图,能够试着根据流程图手写实现$().on()
,下篇文章会放出模拟实现的代码。缓存
1、举例app
<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事件被点击了") })
2、$().on()
(1)进行参数的调整
(2)调用jQuery.event.add()
方法函数
3、jQuery.event.add()
最终调用elem.addEventListener()
来绑定事件
注意:
(1)绑定经常使用的事件(如:click、focus),使用handleObj
保存ui
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保存this
if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; }
(1)、(2)都会初始化事件处理器(addEventListener):spa
//第一次绑定事件,走这里 // 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 ); } } }
4、jQuery的事件绑定为什么不直接绑定在目标元素身上,而是元素和事件分离?
打印$("#A")
设计
console.log($("#A"),'aaaaaa46')
不要在乎jQueryId不一样的问题,每次刷新网页它都会变化code
能够看到对象
jQuery的事件和触发事件的handler是分离的,
事件集合 存在 事件缓存dataPriv
的events
上,索引
//获取数据缓存 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里添加新的绑定事件。
记得看第十一点!
5、guid
的做用?
添加guid
的目的是由于handler
没有直接跟元素节点发生关联,因此须要一个索引来寻找或者删除handler
6、命名空间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")
7、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" }, } }
8、外部是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", } ]
9、为何要使用fix()
来重构 js 的原生 MouseEvent 对象呢?
(1)jQuery 有本身的一套event
处理机制,因此须要符合jQuery
的event
对象
(2)能够传递 data 数据,即用户自定义的数据。
10、trigger的触发机制
$("#A").on("click" ,function (event) { console.log(event,"A被点击了") })
元素#A
自己绑定了一个click
事件,可是click
是原生事件,它是靠 addEventListener
绑定来触发事件的。
可是!jQuery
的trigger
是可以无差异模拟这个交互行为的
$("#A").trigger("click")
从trigger()
的功能上就能够解释 为何jQuery
要设计元素与数据分离了:
若是是直接绑定的话就没法经过trigger
的机制去触发click
事件,
正是由于jQuery
没有直接把事件相关的handler
与元素直接绑定,而是采用了分离处理,
因此咱们经过trigger
触发click
事件与addEventListener
触发click
事件的处理流程是一致的,不一样的只是触发的方式而已。
可是,通trigger
触发的事件是没有事件对象(event)、冒泡(bubble)这些特性的,因此咱们须要有一个功能 能模拟出事件对象,而后生成一个遍历树(eventPath)模拟出冒泡行为,这个就交给了trigger
方法了
关于$().trigger()
的源码解析请看:jQuery源码解析之trigger()
最后,附上本身作的 jQuery
事件绑定到触发全过程的流程图:
(完)