Events

The Enyo event model has been designed to maximize ease of use while still allowing for great expressiveness. The basic syntax for creating events is simple:

components: [
  {kind: "Button", content: "OK", onclick: "okClick"}
],
okClick: function(inSender) {
  // event handler code
}

In this example, we begin by defining a Button object. When the user clicks the OK button, the value of the button's onclick property determines what happens next--in this case, we call the okClick method, which contains code to handle the click.

Notice that the okClick method has an argument called "inSender". This argument refers to the object that generated the event. The event handler's call signature varies between event types, but inSender is always the first argument.

This feature can facilitate the re-use of code. For example, if an application has a number of input controls and must perform some validation whenever one of them changes, one might do the following:

components: [
  {kind: "Input", value: "one", onchange: "validateInput"},
  {kind: "Input", value: "two", onchange: "validateInput"}
],
validateInput: function(inSender) {
  // determine the value of the input that changed
  var value = inSender.getValue();
  if (!this.isValid(value)) {
      // error
  }
}

Event Creation

When creating an event, you'll publish the name of the event in an "events" block, like so:

events: {
  onConfirm: "",
  onCancel: ""
},
components: [
  {kind: "Button", content: "OK", onclick: "okClick"},
  {kind: "Button", content: "Cancel", onclick: "cancelClick"}
],
okClick: function(inSender) {
  this.doConfirm();
},
cancelClick: function(inSender) {
  this.doCancel();
}

When an event is published in an events block, the owning kind is automatically assigned a "do<EventName>" method that, when called, fires the event. In this case, the onConfirm event is fired by calling this.doConfirm, and the onCancel event by calling this.doCancel.

Enyo also has support for chaining published events. This means we can simplify the code in our example by moving doConfirm and doCancel into the button declarations:

events: {
  onConfirm: "",
  onCancel: ""
},
components: [
  {kind: "Button", content: "OK", onclick: "doConfirm"},
  {kind: "Button", content: "Cancel", onclick: "doCancel"}
]

Custom Events and DOM Events

There are two types of events in Enyo--custom events and DOM events. DOM events are the standard DOM events that you know from JavaScript, while custom events are specific to a particular kind or application. When you make new kinds, we strongly encourage you to use custom events, as they are a key part of Enyo's encapsulation strategy.

While custom events and DOM events have a number of features in common, certain significant differences do exist.

For example, a custom event will not automatically bubble beyond the owner of the object that generated the event. (It is possible, however, to bubble custom events manually by chaining them.) By contrast, a DOM event will continue to bubble beyond the owner of the generating object unless explicitly stopped (by having an event handler return true).

Also, while it's technically possible to call "do" methods on published DOM events as well as on published custom events, only the latter usage is common. (As an example, the Control kind publishes onclick, onmousedown, and onmouseup events, but the corresponding do methods are rarely used.)

Another difference between the two types of events is that DOM events may be handled directly by implementing a method with the name "<event>Handler". For example, to handle click events, add a clickHandler method to the kind definition:

clickHandler: function(inSender, inEvent) {
  // I have received a DOM click event, either directly or
  // bubbled up from one of my descendants.
  //
  // If I want to propagate this click to my owner via onclick,
  // I do that here:
  // this.doClick(inEvent);
  //
  // Otherwise, if I choose not to call doClick, my owner won't
  // see the onclick event, and I can handle the click myself
  // within the clickHandler method.
}

Note: When choosing names for event delegate methods for your controls, be careful to avoid the "<event>Handler" naming scheme just described, as this will cause the handler method to be fired twice for each event.

For example, let's say we have a kind that contains the above clickHandler method, and we want to declare a Button as a component. Because of the method naming conflict, the following Button definition results in two calls to clickHandler for each button click.

{kind: "Button", onclick: "clickHandler"}

Instead, choose a different name for your event delegate method, e.g.:

{kind: "Button", onclick: "handleClick"}

This results in a single call to handleClick each time the button is clicked, which is what we want.

More on DOM Events

As illustrated by the previous example, DOM events fire with the signature (inSender, inEvent), where inEvent is the DOM event object.

By default, the following DOM events are handled by Enyo:

  • onmousedown
  • onmouseup
  • onmouseover
  • onmouseout
  • onmousemove
  • onclick
  • ondblclick
  • onchange
  • onkeydown
  • onkeyup
  • onkeypress
  • onblur
  • onfocus
  • onerror (for IMG nodes).

Note that the names of these events have the same case in Enyo as in the DOM itself.

If there are additional DOM events (e.g., onmousewheel) that you want Enyo to handle, do the following:

document.onmousewheel = enyo.dispatch;

Finally, in the (unlikely) event that you need to propagate a DOM event that isn't published (e.g., onmouseover), that can be done using the following syntax:

this.fire("onmouseover", inEvent);