Page MenuHomePhabricator
Diviner Javelin Tech Docs Concepts: Event Delegation

Concepts: Event Delegation
Javelin Technical Documentation (concepts)

Explains Javelin event delegation with JX.Stratcom.

Overview

Javelin provides event delegation as a core feature of the library, orchestrated with JX.Stratcom. Event delegation means that the library listens to every event in the document and then delegates them to handlers you install, as opposed to you installing handlers on specific nodes for specific events you are interested in.

Event delegation can greatly simplify event handling for many types of user interactions, and can also be used to do more traditional event listening for specific events on specific nodes. The goal is to provide a strictly more powerful event model, which gives you a very general delegation toolkit for interactions where delegation makes sense but refines into a very specific toolkit when you need less generality.

Beyond DOM events, Stratcom provides a general event delegation framework which Javelin classes integrate with.

Event Delegation Basics

Javelin routes events based on the event type and sigil set.

The event type is a string with a general description of the event, and includes the DOM event types like 'click' and 'keydown'. It may also be a class-specific event (JX.Duck might emit 'quack').

The sigil set is a list of sigils (see Concepts: Sigils and Metadata) for the event. If the event is a DOM event, Javelin builds the sigil set by walking up the DOM tree from the event target and collecting all the sigils on nodes (it also collects some other data into the sigil set, see "Magic Sigils" below). If the event is a class event, Javelin walks up the class hierarchy collecting class names. If the event is a raw event invoked with JX.Stratcom.invoke(), the caller must provide the sigil set.

When you install an event handler, you specify the event type and the (possibly empty) sigil set you want to listen for.

When an event is invoked, Javelin finds all the listeners for that event type and compares their sigil sets with the event's sigil set. If all of the sigils in a callback's sigil set appear in the event's sigil set, the callback is invoked. Generally, this mechanism allows you to ignore events you are not interested in.

Listening to General DOM Events

You can listen to general DOM events with JX.Stratcom.listen(). This method allows you to select which types of events you want to receive, and which elements must be involved in the event:

JX.Stratcom.listen(
  'click',          // Node
  null,             // Sigil set
  function(e) {     // Callback
    // ...
  });

This listens to all clicks on all elements in the document. More likely, you want to listen only to some clicks. You accomplish this by annotating your document with Javelin sigils (see Concepts: Sigils and Metadata) and specifying a set of sigils which must be present between the target node and the document root. For instance, your document might look like this:

<a href="#" data-sigil="important">Important!</a>
<a href="#">Some Other Link</a>

If you install a handler like the one above, you'll get callbacks for every click, no matter which link it is or even if it's on the document itself. If you just want clicks on the "Important!" link, you can install a more specific handler:

JX.Stratcom.listen(
  'click',
  'important',    // Listen only to this sigil set
  function(e) {
    // ...
  });

Now you will receive a callback only when the event target or some ancestor of it has the "important" sigil, i.e., only when the user clicks on the "Important!" link. You can also specify multiple sigils; ancestors must have all of the sigils for you to get a callback:

JX.Stratcom.listen(
  'click',
  ['menu', 'item'], // Listen only for clicks on menu items.
  function(e) {
    // ...
  });

Listening to Specific DOM Events

You can listen to specific DOM events with JX.DOM.listen(). This method works like JX.Stratcom.listen() but takes a DOM node as the first parameter and listens only for events within that node:

JX.DOM.listen(
  node,             // Node
  'click',          // Event type(s)
  null,             // Sigil set
  function(e) {     // Callback
    // ...
  });

This is similar to setting node.onclick or node.addEventListener, but uses the Javelin delegation core. You can also provide a sigil set, which works just like JX.Stratcom.listen() general events. This is useful if your node is a container, like a <div />, with a lot of stuff in it.

DOM Event Flow

Events dispatched within the DOM propagate using a bubbling method, as detailed by http://www.w3.org/TR/DOM-Level-3-Events/#event-flow Listeners assigned using JX.Stratcom.listen() or JX.DOM.listen() are called in order of the deepest node in the DOM of the nodes which match the sigil set listened to.

In this example the second listener, subscribed to 'inner', will be called before the listener subscribed to 'outer'

<div data-sigil="outer">
  <div data-sigil="inner">
    Click Me
  </div>
</div>

<script type="text/javascript">
JX.Stratcom.listen('click', ['outer'], function(e) { ... });
JX.Stratcom.listen('click', ['inner'], function(e) { ... });
</script>

Listening to Class Events

Beyond DOM events, you can also listen to class events. Every class installed by Javelin has static and instance methods called listen (see JX.Base.listen()). The static method allows you to listen for all events emitted by every instance of a class and its descendants:

JX.Animal.listen(
  'meow',
  function(e) {
    // Listen for ANY 'meow' from any JX.Animal instance or instance which
    // extends JX.Animal.
  });

The instance method allows you to listen for all events emitted by that specific instance:

var cat = new JX.Cat();
cat.listen(
  'meow',
  function(e) {
    // Listen for 'meow' from only this cat.
  });

Magic Sigils

Javelin implements general delegation by building and comparing sigil sets. Some of these sigils are not DOM sigils, but derived from other things:

  • id:* ID sigils are generated when an examined node has an "id" property.
  • obj:* Object sigils are generated when an event affects a class instance.
  • class:* Class sigils are generated while walking an affected instance's class chain.
  • tag:* Tag sigils are generated by examining the tag names of DOM nodes.

For instance, you can listen to all clicks on <a /> tags in a document like this:

JX.Stratcom.listen(
  'click',
  'tag:a',
  function(e) {
    // ...
  });