SproutCore 1.0: SC.Event
SproutCore has always done its own event handling for the most part. In fact, it was one of the first framework to implement a technique called “event delegation” to make event handling significantly faster. In SproutCore 1.0 we’ve improved this event handling with the new SC.Event class.
About Event Delegation
If you’ve ever written regular DHTML-style JavaScript, you know the “standard” way to observe an event is to add an event listener to the DOM element you are interested in. For example, if you want to receive mousedown events from a BUTTON element, you add an event handler to that element. In Prototype you use a method like this:
Event.observe('#my-button', 'mousedown', function() {
... called on event...
});
This approach works fine when you want to enhance a few elements on your page, but when you try to display thousands of contacts or hundreds of photos or emails, it falls apart. Each time you register an event handler on an element, there is a cost. You have to wait for the browser to setup your handler, which can take a lot longer than you might think; especially in IE.
We solved this problem in SproutCore sometime ago using a technique called event delegation. With this approach, SproutCore registers about 6 event listeners on the root document object when your application loads. These listeners are notified anytime any event occurs on the page. From there, SproutCore itself determines which SC.View’s you have created that are interested in receiving these events, if any, and invokes the event handler on that view directly.
How do you indicate interest in receiving an event? It’s simple: just implement the event handler method. For example, let’s say you want to receive the mouseDown event from the same button. Just add the following to your button view class:
mouseDown: function(evt) {
.. called on event ...
}
And you’re done. No setup time. It’s very fast.
Going Further
While event delegation helped speed up event handling in SproutCore, working with events was still not a panacea. The problem is that the events you receive from browsers differs slightly from platform to platform. While SproutCore always normalized this to some degree, it still meant you had to implement platform specific code in your handlers quite a bit.
For SproutCore 1.0, we had to implement some additional event handling code anyway to become DOM-library agnostic, so we decided to improve on this aspect as well by introducing a new class called SC.Event.
SC.Event is a W3C-compliant implementation of the Event interface with some useful additions. All incoming events from the web browser are now wrapped in an SC.Event object before being passed onto your event handler. This means you can now use the standard SC.Event API to work with any incoming event, regardless of platform. This alone will make your code simpler.
In addition, SC.Event makes it much easier to dispatch your own custom events or to dispatch mock events for testing in a cross-platform way. It also simplifies cleanup of event handlers on page unload, which will help to avoid memory leaks in your application.
Using SC.Event
Usually SC.Event will work transparently in the background to make your coding easier. Whenever you implement an event handler (like mouseDown() above), the event object you are passed will be an instance of SC.Event. Simply write your code as if you were working in events in Firefox, and you should be good to go. You can also use some useful additional utility methods defined on the SC.Event class itself such as SC.Event.stop(), which both cancel’s bubbling and prevents the browser’s default action.
If you need to observe an event on a DOM element that is not handled by event delegation, such as the onscroll event, for example, you can also use the SC.Event API to register your own event handler. Just setup your listener like so:
SC.Event.add(SC.$('#my-button'), 'scroll', myObject, myObject.buttonOnScroll);
Note that the add() method takes both a target object and a target object method. This means you can avoid having to call the Function.bind() method that is often required for both jQuery and Prototype code.
If you no longer need to listen to an event, you can remove your observer in a similar way:
SC.Event.remove(SC.$('#my-button'), 'scroll', myObject, myObject.buttonOnScroll);
When your page unloads, all event handlers are automatically removed, so there is no need to do this yourself.
Using SC.Event with Other Libraries
Most DOM-manipulation library provide an API for listening for events. You can use these alternative methods to listen for events yourself if you want and SproutCore will work just fine. If you are integrating a component from YUI, jQuery-UI or any other widget library, there is no need to rewrite their event handling.
However, I strongly recommend that you consider using SC.Event and the Responder API for view’s when writing your own code instead. Since SC.Event is designed specifically for use with SproutCore-style applications and hence it will generally be faster and more efficient for your code.
Getting The Code
SC.Event is part of the new SproutCore 1.0 code base. You can check it out today on the new Bitburger branch in Git. See this wiki page for complete instructions on how to try out the code for yourself.
