5-7 Special Event机制
本节编程练习不计算学习进度,请电脑登录imooc.com操作

Special Event机制

在事件处理中,很多地方都用到了这个 jQuery.event.special 方法,special 是一个在处理特殊的事件相当灵活,可以指定绑定和解开钩子以及定制事件的默认行为,用这个 API 的时候可以创建自定义的事件但不仅仅是执行绑定事件处理程序时,引发这些“特殊”事件可以修改事件对象传递给事件处理程序,引发其他完全不同的事件,或者执行复杂的 setup 和 teardown 代码当事件处理程序绑定到或未绑定元素。

某些事件类型的有特殊行为和属性,换句话说就是某些事件不是大众化的事件不能一概处理。

比如 load 事件拥有特殊的 noBubble 属性,可以防止该事件的冒泡而引发一些错误,所以需要单独针的处理,但是如果都写成判断的形式,显然代码结构就不合理了,而且不方便提供给用户自定义扩展。有些浏览器并不兼容某类型的事件,如 IE6~8 不支持 hashchange 事件,你无法通过 jQuery(window).bind('hashchange', callback) 来绑定这个事件,这个时候你就可以通过 jQuery 自定义事件接口来模拟这个事件,做到跨浏览器兼容。这个就是 special 的作用了,又有点类似之前说的钩子机制了。

原理

jQuery(elem).bind(type, callbakc) 实际上是映射到 jQuery.event.add(elem, types, handler, data) 这个方法,每一个类型的事件会初始化一次事件处理器,而传入的回调函数会以数组的方式缓存起来,当事件触发的时候,处理器将依次执行这个数组。

jQuery.event.add 方法在第一次初始化处理器的时候,会检查是否为自定义事件,如果存在则将会把控制权限交给自定义事件的事件初始化函数,同样事件卸载的 jQuery.event.remove 方法在删除处理器前也会检查此处。

初始化处事件处理器

if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) {
   if (elem.addEventListener) {
      elem.addEventListener(type, eventHandle, false);
   }
}

卸载自定义事件

if (!special.teardown || special.teardown.call(elem, namespaces, elemData.handle) === false) {
    jQuery.removeEvent(elem, type, elemData.handle);
}

jQuery.event.special 对象中,保存着为适配特定事件所需的变量和方法。

beforeunload: Object
blur: Object
click: Object
focus: Object
focusin: Object
focusout: Object
load: Object
mouseenter: Object
mouseleave: Object
pointerenter: Object
pointerleave: Object

事实上 jQuery 自定义事件那些接收的参数有点鸡肋,需要 hack 与能 hack 的事件就那么一点点,且限制颇多,一般情况下很少使用到。

得出总结:

在 jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :

方法中没有传递回调对象是因为回调的句柄被关联到了 elemData,也就是内部数据缓存中了,不难得出 jQuery 的事件绑定机制:jQuery 对每一个 elem 中的每一种事件,只会绑定一次事件处理函数(绑定这个elemData.handle),而这个 elemData.handle 实际只做一件事,就是把 event 丢到 jQuery 内部的事件分发程序。

jQuery.event.dispatch.apply( eventHandle.elem, arguments );

而不同的事件绑定,具体是由 jQuery 内部维护的事件列队来区分(就是那个 elemData.events),在 elemData 中获取到 events 和 handle 之后,接下来就需要知道这次绑定的是什么事件了。

任务

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. <script src="http://www.imooc.com/static/lib/jquery/1.9.1/jquery.js" type="text/javascript"></script>
  6. <title>Special Event机制</title>
  7. </head>
  8. <body>
  9.  
  10. <p>通过special模拟input事件(IE67测试)</p>
  11. <div id="aaron" style="border:1px solid red;width:200px;">
  12. <input type="text" />
  13. </div>
  14. <p>输入结果:</p>
  15. <div id="ret"></div>
  16.  
  17.  
  18. <script type="text/javascript">
  19.  
  20. /*
  21. * 让jQuery支持自定义一个事件
  22. * IE6\7\8不支持input事件,但支持propertychange事件
  23. * 通过special模拟
  24. */
  25. (function ($) {
  26. if ('onpropertychange' in document) {
  27. // 检查是否为可输入元素
  28. var rinput = /^INPUT|TEXTAREA$/,
  29. isInput = function(elem) {
  30. return rinput.test(elem.nodeName);
  31. };
  32.  
  33. //元素就通过别的相近的事件处理,
  34. //然后通过一些条件判断从而达到模拟
  35. $.event.special.input = {
  36. setup: function() {
  37. var elem = this;
  38. if (!isInput(elem)) return false;
  39. $.data(elem, '@oldValue', elem.value);
  40. $.event.add(elem, 'propertychange', function(event) {
  41. // 元素属性任何变化都会触发propertychange事件
  42. // 需要屏蔽掉非value的改变,以便接近标准的onput事件
  43. if ($.data(this, '@oldValue') !== this.value) {
  44. $.event.trigger('input', null, this);
  45. };
  46.  
  47. $.data(this, '@oldValue', this.value);
  48. });
  49. },
  50. teardown: function() {
  51. var elem = this;
  52. if (!isInput(elem)) return false;
  53. $.event.remove(elem, 'propertychange');
  54. $.removeData(elem, '@oldValue');
  55. }
  56. };
  57. };
  58.  
  59. // 声明快捷方式:$(elem).input(function () {});
  60. $.fn.input = function(callback) {
  61. return this.bind('input', callback);
  62. };
  63.  
  64. })(jQuery);
  65.  
  66.  
  67.  
  68. var ret = $("#ret")
  69.  
  70. $("#aaron").bind('input', function () {
  71. ret.append("<li>"+ arguments[0].target.value +"</li>")
  72. });
  73.  
  74.  
  75. </script>
  76.  
  77. </body>
  78. </html>
下一节