10.5 扩展事件

诸如mouseenterready这样的事件,都是jQuery内部的特殊事件。这些事件使用了jQuery精心设计的事件扩展框架,可以在事件处理程序生命周期的不同时间点上执行。它们可以对被绑定或被反绑定的事件处理程序作出反应,甚至可以像被单击的链接和被提交的表单一样具有可阻止的默认行为。利用这些事件扩展API,可以创建出与原生DOM事件非常类似的新事件。

代码清单10-13中针对滚动实现的节流行为十分有用,可以将其一般化,以便在其他项目中重用。为此,我们可以创建一个特殊的新事件,用它来封装相应的节流技术。

为了实现一个事件的特殊行为,需要为$.event.special对象添加属性。这个属性的键是我们的事件名称,而它的值本身是一个对象。这个特殊的事件对象包含可以在不同时刻调用的回调函数::

  • add会在每次为当前事件绑定处理程序时调用;

  • remove会在每次为当前事件删除处理程序时调用;

  • setup会在为当前事件绑定处理程序,且没有为元素的这个事件绑定其他处理程序时调用;

  • teardownsetup的反操作,会在某个元素删除这个事件的最后一个处理程序时调用;

  • _default是当前事件的默认行为,在没有被事件处理程序阻止的情况下会执行。

在使用这几个回调函数时,可以充分发挥我们的创造力。接下的例子将会探讨一种非常常见的情况,那就是响应某些浏览器条件而自动触发事件。如果没有处理程序监听事件,那么监视状态并触发事件就是一种浪费。所以,我们可以通过setup回调函数来实现只在必要时进行初始化,参见代码清单10-16。

代码清单10-16

  1. (function($) {
  2. $.event.special.throttledScroll = {
  3. setup: function(data) {
  4. var timer = 0;
  5. $(this).on('scroll.throttledScroll', function(event) {
  6. if (!timer) {
  7. timer = setTimeout(function() {
  8. $(this).triggerHandler('throttledScroll');
  9. timer = 0;
  10. }, 250);
  11. }
  12. });
  13. },
  14. teardown: function() {
  15. $(this).off('scroll.throttledScroll');
  16. }
  17. };
  18. })(jQuery);

对于这个滚动节流事件,我们需要绑定常规的scroll处理程序,该处理程序使用与代码清单10-14中用到的相同的setTimeout技术。每当计时器结束时,就会触发这个自定义事件。每个元素只需要一个计时器,所以setup回调函数可以满足我们的要求。通过以scroll处理程序作为命名空间,可以在teardown被调用时轻松地删除相应的处理程序。

为了使用这个事件,我们要做的就是像下面这样为throttledScroll绑定处理程序。这样不仅极大地简化了绑定事件的代码,同时还实现了一个非常方便的可重用的节流机制,参见代码清单10-17。

代码清单10-17

  1. (function($) {
  2. $.event.special.throttledScroll = {
  3. setup: function(data) {
  4. var timer = 0;
  5. $(this).on('scroll.throttledScroll', function(event) {
  6. if (!timer) {
  7. timer = setTimeout(function() {
  8. $(this).triggerHandler('throttledScroll');
  9. timer = 0;
  10. }, 250);
  11. }
  12. });
  13. },
  14. teardown: function() {
  15. $(this).off('scroll.throttledScroll');
  16. }
  17. };
  18.  
  19. $(document).on('mouseenter mouseleave', 'div.photo', function(event) {
  20. var $details = $(this).find('.details');
  21. if (event.type == 'mouseenter') {
  22. $details.fadeTo('fast', 0.7);
  23. } else {
  24. $details.fadeOut('fast');
  25. }
  26. });
  27.  
  28. $(document).on('nextPage', function(event, scrollToVisible) {
  29. var url = $('#more-photos').attr('href');
  30. if (url) {
  31. $.get(url, function(data) {
  32. var $data = $(data).appendTo('#gallery');
  33. if (scrollToVisible) {
  34. var newTop = $data.offset().top;
  35. $(window).scrollTop(newTop);
  36. }
  37. checkScrollPosition();
  38. });
  39. }
  40. });
  41.  
  42. var pageNum = 1;
  43. $(document).on('nextPage', function() {
  44. pageNum++;
  45. if (pageNum < 20) {
  46. $('#more-photos').attr('href', 'pages/' + pageNum + '.html');
  47. }
  48. else {
  49. $('#more-photos').remove();
  50. }
  51. });
  52.  
  53. function checkScrollPosition() {
  54. var distance = $(window).scrollTop() + $(window).height();
  55. if ($('#container').height() <= distance) {
  56. $(document).trigger('nextPage');
  57. }
  58. }
  59.  
  60. $(document).ready(function() {
  61. $('#more-photos').click(function(event) {
  62. event.preventDefault();
  63. $(this).trigger('nextPage', [true]);
  64. });
  65.  
  66. $(window)
  67. .on('throttledScroll', checkScrollPosition)
  68. .trigger('throttledScroll');
  69. });
  70. })(jQuery);

深入学习特殊事件

虽然本章主要介绍与处理事件相关的高级技术,但创建特殊事件则是更加高级的技术,详细地讨论它超出了本书的范畴。前面展示的throttledScroll的例子只是这种技术最简单、最常见的用法。其他可能的应用包括:

  • 修改事件对象,以便事件处理程序可以使用不同的信息;

  • 让DOM中的某个地方发生的事件触发与不同元素关联的行为;

  • 对新的浏览器特有的非标准DOM事件作出响应,让jQuery代码像处理标准事件一样处理它们;

  • 改变处理事件冒泡和事件委托的方式。

这些任务都有可能相当复杂。如果想更深入地了解事件扩展API给我们提供的可能性,建议大家阅读jQuery学习中心的文档: http://learn.jquery.com/events/event-extensions/