How Custom Events Will Save the Universe

20,024 views
19,117 views

Published on

Delivered at TXJS (http://texasjavascript.com) on June 5, 2010.

All the major frameworks support custom events. All framework authors love custom events and want you to use them. But few people actually do.

Custom events aren’t a gimmick. The ability to fire your own events — just like the browser fires click, mousemove, focus, and all the rest — can solve many of the problems large JS projects face.

We’ll look at a handful of recipes for integrating custom events into your own code. You’ll learn how to fire custom events in each of the major JavaScript frameworks, and explore the advantages that custom events have over a standard callback pattern.

Published in: Technology, Business
3 Comments
29 Likes
Statistics
Notes
  • What I didnt understand from your slideshow is how custom-events make debugging easier. From my humble experience, stepping through a debugger with code that contains one or more document.fire / observe can be very cumbersome because it beomes harder to follow the flow of my code execution.
    People tend to abuse custom events for ’coupling’ object. In my opinion custom events tend to introduce bad design.. not because custom events are poorly designed but they are overused and applied in situations they shouldnt be. The reason for this probably being that they are so convenient to use. As for the project I am currently working on its littered with custom events shooting back and forward between modules and objects. It’s a nightmare to debug.. and the same can be true for unit testing when applied in a unresponsible fashion.. which often tends to be the case. Not to mention what custom events do to the readibility of your code..
    As far as document acting as an event bus, it would be more useful if you could actually see what events are send through it.. as far as I know you completely rely on good documentation.
    So overall, i very much doubt that custom events will save the universe. Maybe this was true if all developers had great programming and design skill like yourself ;-)
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Andreas: I addressed this in greater detail in the talk. In Prototype, 'document' acts as the 'event bus' for all events, whether DOM-related or not. This is an opinionated choice, but one that (in my opinion) pays dividends in simplicity. One is free to write a separate event bus for non-DOM events; nearly all of the material covered in the slides will still be applicable.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • I think this is a needless coupling of view and presentation layer, that makes it hard to reuse your code in another situation, cause you have to pay attention about the DOM all the time.
    In mootools (not sure about other frameworks) every 'Class' object that implements Events can listen events or fire events to another 'Class' object . None of them needs to know parts of the DOM that has nothing to with them. Every object just need to know the one DOM object its related to (a button, a select ...) and the eventBus object. So when you use the widget in another situation or something in the html page has changed there is only one 'Class' to change and not every part that use this dom object as an event observer.
    There is a nice talk by Nicholas Zakas (http://www.slideshare.net/nzakas/scalable-javascript-application-architecture) and a talk at googles io conference (http://code.google.com/events/io/2010/sessions/architecting-production-gwt.html) about this topic. Also the book 'Pro JavaScript Design Patterns' (http://apress.com/book/view/159059908x) described how to build the observer pattern by yourself.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
20,024
On SlideShare
0
From Embeds
0
Number of Embeds
4,787
Actions
Shares
0
Downloads
156
Comments
3
Likes
29
Embeds 0
No embeds

No notes for slide

How Custom Events Will Save the Universe

  1. How CUSTOM EVENTS will save the universe
  2. Law of Demeter
  3. “Each unit should have only limited knowledge about other units.”
  4. (we break this rule all the time)
  5. the Law of Demeter is about removing unnecessary coupling
  6. custom events can help decouple your code
  7. Broadcaster and receiver don’t have to know about one another…
  8. … so there’s far less to mock in your unit tests
  9. YOU DO HAVE UNIT TESTS, RIGHT?
  10. How do custom events work?
  11. $(document).observe('custom:event', function(event) { var customData = event.memo; // stuff }); $('troz').fire('custom:event', { foo: "bar" });
  12. $(document).bind('customevent', function(event, customData) { // stuff }); $('#troz').trigger('customevent', [{ foo: "bar" }]);
  13. $(document).addEvent('customevent', function(event, customData) { // stuff }); $('troz').fireEvent('customevent', { foo: "bar" });
  14. BUT WHY?
  15. Why? Makes testing easier (maybe you’ll do it now)
  16. Why? Rapid prototyping
  17. Why? More versatile than callbacks
  18. Why? Can be bubbled/canceled
  19. Why? Can handle exceptions properly (in theory)
  20. (quoth Dean Edwards)
  21. In Prototype, exceptions raised in one handler won’t affect another handler
  22. (is this a big deal? smart people disagree)
  23. CASE STUDIES
  24. Case Studies # 1
  25. Case Studies (script.aculo.us 2.0)
  26. scripty2 uses custom events… …as a “metronome” for effects
  27. window.setTimeout(function() { document.fire('effect:heartbeat'); }, 0);
  28. document.observe('effect:heartbeat', advanceEffectByOneFrame);
  29. Seems pointless… …until you need to debug
  30. Step through an animation frame by frame document.observe('keydown', function(event) { if (event.keyCode === Event.KEY_RIGHT) { document.fire('effect:heartbeat'); } });
  31. scripty2 uses custom events… …to pass messages between UI widgets
  32. S2.UI.Menu (used by other S2.UI components)
  33. var menu = new S2.UI.Menu(); menu.addChoice("Foo"); menu.addChoice("Bar"); someElement.insert(menu); menu.open();
  34. S2.UI widgets act like elements when needed, so... menu.observe('ui:menu:selected', function(event) { console.log('user clicked on:', event.memo.element); });
  35. Easy to use in any context
  36. Button with menu
  37. ASIDE: Custom events are cancelable
  38. var event = $('troz').fire('custom:event'); if (!event.stopped) performSomeDefaultAction();
  39. (prevent all menus from appearing) document.observe('ui:menu:before:open', function(event) { event.stop(); });
  40. scripty2 uses custom events… …as hooks for debugging
  41. “Why aren’t these effects queueing like I expect?”
  42. document.observe('effect:dequeued', function(event) { var queue = event.memo; console.log("Effects in queue:", queue.size()); });
  43. You get debugging FOR FREE
  44. Case Studies # 2
  45. Case Studies Mouse wheel
  46. http://adomas.org/javascript-mouse-wheel/ window.addEventListener('DOMMouseScroll', handler); window.onmousewheel = handler; function handler(event) { var delta; if (event.wheelDelta) { delta = event.wheelDelta / 120; if (window.opera) delta = -delta; // (not a joke) } else if (event.detail) { delta = -event.detail / 3; } // Do stuff with your stupid delta }
  47. Instead, do this:
  48. window.addEventListener('DOMMouseScroll', handler); window.onmousewheel = handler; function handler(event) { var delta; if (event.wheelDelta) { delta = event.wheelDelta / 120; if (window.opera) delta = -delta; // (not a joke) } else if (event.detail) { delta = -event.detail / 3; } // Fire a custom event with the normalized delta. var result = event.target.fire('mouse:wheel', { delta: delta }); if (result.stopped) event.preventDefault(); }
  49. See also: hashchange
  50. Case Studies # 3
  51. Case Studies User idle state
  52. http://perfectionkills.com/detect-idle-state-with-custom-events/ credit: kangax document.observe('state:idle', function() { turnOffBackgroundAjaxRequests(); }); document.observe('state:active', function() { turnOnBackgroundAjaxRequests(); });
  53. Case Studies # 4
  54. Case Studies Keyboard events
  55. (function() { var KEYS = {}; KEYS[Event.KEY_ESC] = "esc"; KEYS[Event.KEY_UP] = "up"; KEYS[Event.KEY_DOWN] = "down"; KEYS[Event.KEY_LEFT] = "left"; KEYS[Event.KEY_RIGHT] = "right"; // ... and so on function handler(event) { if (event.keyCode && KEYS[event.keyCode]) { event.element().fire("key:" + KEYS[event.keyCode], event); } } document.observe("keydown", handler); })();
  56. Then you’d be able to do something like this: document.observe('key:left', function() { moveSlide(-1); }); document.observe('key:right', function() { moveSlide( 1); });
  57. Case Studies # 5
  58. Data broadcasting
  59. Server-sent events are awesome… …but not universally supported
  60. for browsers that support server-sent events var eventSource = $('event_source'); eventSource.observe('server-sent-event-name', function(event) { document.fire('data:received', event.data); });
  61. for browsers that don’t new Ajax.Request('/legacy/polling', { onComplete: function(request) { document.fire('data:received', request.responseJSON); } });
  62. observer works with either approach $(document).observe('data:received', function(event) { doStuffWithYourData(event.memo); });
  63. …and your unit tests can fire dummy data function testThatSomethingHappened() { document.fire('data:received', FIXTURE_DATA); assertSomething(); }
  64. FAQ
  65. FAQ “What if my events aren’t DOM-related?”
  66. (meh)
  67. Use the DOM anyway, I say
  68. FAQ “Isn’t this overkill?”
  69. Yes, sometimes
  70. FAQ “Aren’t events slow?”
  71. NO
  72. Events aren’t slow; they’re synchronous
  73. Events are only as slow as the handlers attached to them
  74. If performance is a concern, defer window.setTimeout(function() { document.fire('costly:custom:event'); }, 0);
  75. QUESTIONS? Andrew Dupont http://andrewdupont.net

×