Dojo 1.7 includes a rewrite of the event handling system and introduces a new module, dojo/on, to provide lighter-weight, faster, more succinct, and more direct event handling. The new event system is highly optimized for mobile applications, with a lightweight footprint and cross-platform touch event normalization. While Dojo continues to provide backwards compatibility with dojo.connect, the dojo/on module eliminates much of code that is rarely used in dojo.connect and can be loaded in 5.2KB minified, half the size of dojo.connect, and fires non-native events twice as fast. The dojo/on module also includes new functionality for extension events, dispatching events, and providing a base class/mixin for adding event emitting functionality to classes. In addition, there is now a new on() method for NodeLists (the results of dojo.query()) based on the new event listening with event delegation.

Using dojo/on

dojo/on has a similar API to dojo.connect. You still provide a target object or DOM node, the event name to listen for, and the listener. Dojo 1.7 now has full support for the AMD module format, so we will get a reference to the on function directly from the dependency list, and then use the on function:

define(["dojo/on"], function(on){
  var target = dojo.byId("target-id");
  on(target, "click", function(event){
    // executed when target is clicked
  });
});

The on function is also similar to dojo.connect in providing essential event normalization. The returned events should have standard W3C event properties including target and currentTarget and methods including stopPropagation() and preventDefault(), even on Internet Explorer (back to version 6). dojo/on does separate out some of the less used and non-standard keypress and mouseenter/leave normalization as extension events (it is still incorporated in dojo.connect for backwards compatibility).

Event Delegation

One of the exciting new features with the new event handling system is the addition of event delegation support in Dojo base/core. Event delegation is a powerful mechanism, where we register a single event listener on the DOM to handle events that can bubble up from multiple sources. The new event delegation system makes it easy to select acceptable DOM nodes to accept propagating events from, and properly react to. Event delegation has existed in Dojo previously in dojox/NodeList/delegate.js, but with support in the core event system, delegation is now very convenient to use. You provide an event name, colon prefixed with the a CSS selector to specify which nodes can bubble to the listener. The listener will be called if the an element inside the target element fires an event and that element matches the CSS selector in the scope of the target element. When the listener is called, the this object will be set to the child element that matched the selector. For example, if we had a form with several <button> elements on it, we could listen for click events on any of the buttons with:

form = dojo.byId("my-form");
on(form, "button:click", function(event){
  // event -> The event object
  // this -> The button element
});

Or we could listen for change events on an text inputs within the <div> with a class of “details”:

on(form, 'div.details input[type="text"]:change', changeHandler);

We can also comma-delimit multiple events to listen to. For example, to listen to submit events on the form and clicks on <button> elements, we could do:

on(form, "submit, button:click", submitHandler);

NodeList’s on()

One of ways to use the new event listening module is by using the on() method for NodeList‘s returned from dojo.query. The on() method is based on dojo/on, and gives us access to all the new features and performance, including event delegation. We use NodeList's on() just like Dojo on() except that we don’t need to provide a node as the first argument, all the nodes in the NodeList are targeted. For example, we could query for all <button> elements and listen for clicks:

define(["dojo/query"], function(query){
  query("button").on("click", clickHandler);
  ...

We could also use event delegation, here getting any <form> elements and listening for clicks on any <button> children:

query("form").on("button:click", clickHandler);

Extension Events

Another new feature of dojo/on is support for extension events, allowing for listening to custom events that may be aggregates of multiple events or emulation of events not supported by the browser. Dojo 1.7 now includes a whole library of gesture events that are extension events, like swipe, tap, and more. These events are particularly useful for mobile applications where high-level events are composed from low-level events. For example, to listen for a swipe event, we could do:

define(["dojo/on", "dojox/gesture/swipe"], function(on, swipe){
  on(node, swipe.left, function onSwipeLeft(){
    // called on a swipe left
  });
});

The swipe gestures are an example of an extension event that is based on other events (touchstart, touchmove, touchend).

Legacy Code as Extensions

To reduce the size of dojo/on and improve modularity, there are several other event emulation mechanisms there were moved out of the connect.js module into extension events (these are still included by connect.js for backwards compatibility), including extension events based on IE’s non-bubbling mouseenter and mouseleave events, (unfortunately the standard mouseover and mouseout do bubble, which makes them painful to use). You can use the use enter and leave extension events from dojo/mouse to emulate IE’s events cross-browser:

define(["dojo/on", "dojo/mouse"], function(on, mouse){
  on(node, mouse.enter, function hover(){
    dojo.addClass(node, "hovering");
  });
  on(node, mouse.leave, function unhover(){
    dojo.removeClass(node, "hovering");
  });
});

There is also an extension event for emulating Firefox’s keypress behavior, but this emulated behavior is not terribly helpful, and using this emulator is not recommended and exists purely for backwards-compatibility (it is used to retain dojo.connect‘s current behavior). Even with the dojo.connect API and the extension events for full backwards compatibility, the total module size is still smaller than the dojo.connect module from Dojo 1.6.

Touch Normalization

The new event system now includes normalization for standard touch events. dojo/on (dojo.connect) now ensures that touch events include the standard pageX, pageY, rotation, scale, and other touch properties on all devices. It also normalizes the orientationchange on non-conformant Android devices. You can listen for touch events like any other standard event:

on(node, "touchstart", function(){
  // called on a touchstart event
});

Emitting Events

The dojo/on module also adds support for emitting custom events using native browser event delegation mechanisms. The new on.emit function can be used to create custom native events that can trigger DOM listeners, and includes support for bubbling and cancelling. This is used in Dojo Mobile to create transition events, for example.

Faster, Lighter

The new dojo/on module is a compact, fast module with minimal dependencies designed for lightweight mobile applications. Along with these new features, a significant amount of heavy legacy code has been left out of dojo/on, and moved out to separate modules. The dojo/on module also leverages Dojo’s new has() feature branching capabilities for creating small lightweight, browser-specific builds.

This blog post should give you an introduction to the new capabilities of dojo/on, you can refer to the dojo.on documentation for more information.