jquery event trigger 分析

  1. 注册了事件,如onclick。那么当用户点击这个元素时,就会自动触发这个事件的已经注册的事件处理函数。但是我们有的时候要采用程序来模拟事件的触发就得采用强迫触发某个事件。在IE中我们可以采用.fireEvent()来实现。如:<form onsubmit="a()" >中,如果button的form.submit()的方式提交表单,是不会主动触发onsumbit事件的,如果必须的话,就要在submit前$(“:form”)[0].fireEvent("onsubmit”,),这样就会触发该事件。
  2. 在mozilla中有三个步骤: var evt = document.createEvent('HTMLEvents');
  3. evt.initEvent('change',true,true); t.dispatchEvent( evt );
  4. 在prototype是采用这样的方式来实现的。那么jquery中呢,它的实现方式有一点不一样。
  5. trigger : function(type, data, fn) {
  6. returnthis.each(function() {
  7. jQuery.event.trigger(type, data, this, true, fn);
  8. }); },
  9. Trigger有三个参数,data参数是为了注册的事件函数提供了实传。如果data[0]中preventDefault存在,data[0]就可以做为用户自定义的包裹事件的空间。Fn是可以为事件提供一个即时即用的事件处理方法。也就是在没有注册事件的情况下也可以通过传入处理函数来处理事件。如果已经注册了,那就是在原来的事件处理函数之后执行。
  10. //这个方法将会触发指定的事件类型上所有绑定的处理函数。但不会执行浏览器默认动作.
  11. triggerHandler : function(type, data, fn) {
  12. returnthis[0]&& jQuery.event.trigger(type,data,this[0],false,fn);
  13. },
  14. triggerHandle通过把jQuery.event.trigger的donative参数设为false,来阻止执行浏览器默处理方法。它与trigger不现的一点,还在于它只是处理jquery对象的第一个元素。
  15. 上面两个方法都调用了jQuery.event.trigger来完成任务:
  16. trigger : function(type, data, elem, donative, extra) {
  17. data = jQuery.makeArray(data);//data可以为{xx:yy}
  18. //支持getData!这样的形式,exclusive = true表现会对add的注册的
  19. //事件的所有函数进行命名空间的分种类的来执行。
  20. if (type.indexOf("!") >= 0) { ①
  21. type = type.slice(0, -1);var exclusive = true;
  22. }
  23. if (!elem) {// 处理全局的fire事件 ②
  24. if (this.global[type])
  25. jQuery.each(jQuery.cache, function() {
  26. // 从cache中找到所有注册该事件的元素,触发改事件的处理函数
  27. if (this.events && this.events[type])
  28. jQuery.event.trigger(type, data, this.handle.elem);
  29. });
  30. } else {// 处理单个元素事件的fire事件 ③
  31. if (elem.nodeType == 3 || elem.nodeType == 8) return undefined;
  32. var val, ret, fn = jQuery.isFunction(elem[type] || null),
  33. // 如果data参数传进入的不是浏览器的event对象的话,event变量为true.
  34. //如果data参数本身是娄组,那么第一个元素不是浏览器的event对象时为true.
  35. //对于event为true。即没有event传进入,先构建一个伪造的event对象存在data[0]。
  36. event = !data[0] || !data[0].preventDefault;
  37. // 在没有传入event对象的情况下,构建伪造event对象。
  38. if (event) {//存到数组中的第一个 ④
  39. data.unshift( { type : type,target : elem,
  40. preventDefault : function() {},stopPropagation :
  41. function() {}, timeStamp : now() });
  42. data[0][expando] = true; // 不需要修正伪造的event对象
  43. }
  44. data[0].type = type; //防止事件名出错
  45. //表现会进行事件注册函数的分类(命名空间)执行。不是所有的。
  46. if (exclusive) data[0].exclusive = true;
  47. //与prototype等传统的处理方式不一样,没有采用fireEvent来
  48. //来fire通过注册到浏览器事件中的事件处理方法。
  49. //这里分了三步,先fire通过jQuery.event.add来注册的事件,这个事件
  50. //有可能是自定义的事件(没有注册到浏览器事件中)。
  51. //第二步是fire通过elem.onclick方式注册的事件的本地处理函数
  52. //第三步是fire默认的事件处理方式(在本地的onclick的方式注册
  53. //不存在的情况下)。
  54. // 这里是触发通过jQuery.event.add来注册的事件,
  55. var handle = jQuery.data(elem, "handle"); ⑤
  56. if (handle)val = handle.apply(elem, data); //这里data分成多个参数
  57. //处理触发通过elem.onfoo=function()这样的注册本地处理方法,
  58. //但是是对于links 's .click()不触发,这个不会执行通过addEvent
  59. //方式注册的事件处理方式。
  60. if ((!fn || (jQuery.nodeName(elem, 'a') && type == "click")) ⑥
  61. && elem["on"+ type]&& elem["on"+type].apply(elem,data) === false)
  62. val = false;
  63. //额外的函数参数的开始几个是通过data给定的。这里会把伪造加上的event给去掉。
  64. //它的最后一个参数是一系列的事件处理函数返回的结果,一般为bool值
  65. //这个函数可以根据这个结果来处理一个扫尾的工作。
  66. if (event) data.shift();
  67. // 处理触发extra给定的函数处理。
  68. if (extra && jQuery.isFunction(extra)) { ⑦
  69. ret = extra.apply(elem, val == null ? data : data.concat(val));
  70. //如果这个函数有返回值,那么trigger的返回值就是它的返回值
  71. //没有的话就是串连的事件处理函数的最后一个返回值。一般为bool
  72. if (ret !== undefined) val = ret;
  73. }
  74. // 触发默认本地事件方法,它是在没有如.onclick注册事件
  75. //加上前面的执行事件处理函数返回值都不为false的情况下,才会执行。
  76. //它还可以通donative来控制是否执行。
  77. //如form中可以采用this.submit()来提交form.
  78. if (fn && donative !== false && val !== false
  79. && !(jQuery.nodeName(elem, 'a') && type == "click")) {
  80. this.triggered = true;
  81. try {elem[type](); //对于一些hidden的元素,IE会报错
  82. } catch (e) {}
  83. }
  84. this.triggered = false;
  85. }
  86. return val;
  87. },
  88. Jquery的fire事件的方法与prototype中实现是完全不一样的。Ext、YUI没有提供强迫触发事件的方法。对于一般的思维,程序来触发浏览器的事件就应该采用fireEvent或dispatchEvent方法来运行。
  89. 但是jquery采用一种不同的方法。对于通过jquery.event.add来注册的事件(不管是自定义的还是注册到浏览器事件),它保存在一个与元素及事件名相对应的cache中。在浏览器的触发中,这个是没有什么作用。但是它是为了通过等程序来强迫触发时,从cache中取到对应的事件处理函数。这个时候就抛开了浏览器的事件。在这里还可以执行一些自定义的事件函数。如⑤处。
  90. 对于通过html的标签中如click或elem.onclick=function(){}形式注册的事件函数。在⑥处它采用执行元素的如onclick形式的回调函数就可以。通过这种dom0的方式只能注册一个函数。
  91. 有的时候,如果没有onclick这样的事件处理函数,浏览器会执行默认的处理函数。如form.submit()。⑧处可以看出对于这样的默认的事件处理,还可以通过参数donative来控制。
  92. 程序手动强迫触发事件,有一点问题就是event是怎么生成,就是没有浏览器生成event传入到函数中。Prototype采用了是新生成的dataavailable的事件。这样的事件也没有什么作用。Jquery也采用fake的方式伪造一个一个事件,如④,它比prototype的事件好处在于它能通过trigger的函数的参数来传入需要的event。Prototype则不能。
  93. 通过上面的分析,隐隐可以看出Jquery是通过模拟浏览器的触发事件的执行过程来构建这个trigger的函数的。先执行dom1方式(addEvent)注册的事件,再执行dom0方式注册的事件,最后看看要不要执行默认的事件处理。
  94. 在⑦处,我们可以看出trigger还可能通过传入回调函数和参数来完成对执行的事件处理函数的结果进行判断处理,形成新结果通过trigger的函数返回。这在有的时候是很有用的。
  95. 除了这些,它还能对于事件的处理函数进行分类(namespace),可以在合适的时候调用事件的不同分类的的处理函数(通过jquery.event.add来注册)。这个分类的处理在handle实现。
  96. handle : function(event) {
  97. // 返回 undefined or false
  98. var val, ret, namespace, all, handlers;
  99. //修改了传入的参数,这里是引用。
  100. event = arguments[0] = jQuery.event.fix(event || window.event);
  101. // 命名空间处理
  102. namespace = event.type.split(".");
  103. event.type = namespace[0];
  104. namespace = namespace[1];
  105. // all = true 表明任何 handler,namespace不存在,同时
  106. //event.exclusive不存在或为假时,all=true.
  107. all = !namespace && !event.exclusive;
  108. // 找到元素的events中缓存的事件名的处理函数列表
  109. handlers = (jQuery.data(this, "events") || {})[event.type];
  110. for (var j in handlers) {// 每个处理函数执行
  111. var handler = handlers[j];
  112. // Filter the functions by class
  113. if (all || handler.type == namespace) {
  114. // 传入引用,为了之后删除它们
  115. event.handler = handler;
  116. event.data = handler.data;//add的时候加上的
  117. ret = handler.apply(this, arguments);// 执行事件处理函数
  118. if (val !== false)
  119. val = ret;// 只要有一个处理函数返回false,本函数就返回false.
  120. if (ret === false) {// 不执行浏览器默认的动作
  121. event.preventDefault();
  122. event.stopPropagation();
  123. }
  124. }
  125. }
  126. return val; },
  127. handle的主要功能是就是分类且有序地执行事件的所有的注册的处理函数。