English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

Developing a Twitter Flight Edge

2. The DOM Event Model

Twitter Flight is a component library designed around one major aspect of the Document Object Model: the Event Model. Communication between individual components on the page is achieved via custom DOM events, facilitated by the jQuery Event API. As such, a good understanding of the fundamentals will help you write better and more idiomatic code with Twitter Flight. In this chapter we'll cover the Event Interface to discuss the data made available by events, then cover Event Bubbling, the core behavior that drives Flight. Flight also exposes an interface to define default actions, a concept native to events but not exposed to JavaScript. We'll also touch on the key differences between the jQuery events used by Flight and native DOM events. Once we understand the driving concepts behind Flight, we can start writing our RSS reader application.

DOM Event Interface

Native DOM Events have a number of attributes you may find useful in determining which course of action to take when responding to an event:

  • type: The case-insensitive name of the event. Often, this information is superfluous; attaching listeners to DOM elements requires the event name, so a given function is only invoked when an event of a specific type is fired. This property is more useful when using the EventListener Interface, where dispatching the event on the type attribute is more common. Since Twitter Flight does not use the EventListener Interface, this property is largely ignored.
  • target: The DOM element to which the event was originally dispatched. Since events propagate up the stack from the event target through each of its ancestors, this property is very useful in determining which element the event describes.
  • currentTarget: The DOM element to which the event listener was attached. Since the same function may be bound to multiple elements, this property gives context to which element is currently processing the event.

The difference between target and currentTarget is subtle but important, particularly to event delegation of which Flight makes extensive use. Event listeners need not be bound directly to the source of an event; by default, Flight will bind a component's callbacks to the DOM node it is attached to. Take, for example, the following markup:

    <div class="component-root">
        <button class="clickable"></button>
    </div>

If we bind a component to the div.component-root element, define a callback that targets the selector .clickable, and then click on the button, the event object passed to the callback function will have the button as the target, and the div.component-root element as the currentTarget. The event will call attached click event listeners on all nodes containing the target, starting at the button. The event.target property will stay the same in each invocation, but currentTarget will always refer to the node to which the event listener is attached.

If this is still confusing, don't worry; often, we'll only need to inspect event.target, since event.currentTarget will almost always be the component node or document.

Event Bubbling

The DOM Event Model specifies a number of useful properties for events, but none are as important to cross-component communication as event bubbling. In short, event bubbling means that an event triggered at a given DOM Element will notify all ancestor nodes of the event and its original target. The DOM uses this behavior natively to compose some of the more complex behaviors; for example, a keypress event for the enter key in an <input> element can cause an ancestor <form> to submit.

This offers two major advantages to libraries like Twitter Flight that rely on the event system. First, components can be isolated from one another; as long as one component is a descendant of another component, it can communicate to the parent component without knowledge of any of the parent component's functionality. Second, components can be composed into a fully-decoupled hierarchy, with complex components using the functionality of one or more smaller components and listening for emitted events.

Events do not always bubble. Some events, like focus/blur and mouseenter/mouseleave, are triggered at an element and ancestor elements are not notified of the event. Additionally, event listeners may halt the bubbling behavior of an event by calling stopPropagation on the Event object. Ancestor elements of the listening element will not be notified of the event. A common pitfall when using stopPropagation is the expectation that all further event listeners will not be called. A different method, stopImmediatePropagation, accomplishes this task; while it is not natively implemented in all browsers, jQuery Events provides a polyfill for this behavior.

Default Actions

Some native DOM Events have an associated "default action" that runs when an event occurs. These range from simply toggling the checked attribute on a checkbox-type input element to following a link's href attribute, and even submitting form data to a server. However, these actions do not always run; event listeners can call the preventDefault method on the Event object to stop the default action from occurring after an event is triggered.

The HTML5 Constraints specification is a native example of preventing default behavior. <input> elements may define various attributes to validate user input, such as a min and max attribute for numeric input or a maxlength to cap the length of the user input. When a user provides input that violates the constraints on a given <input> element, attempts to submit the form will have the default action (sending the form data to the server) prevented and will notify the user that certain fields have invalid values.

Being able to prevent default behavior has more use than just form validation. One of the major trends in web development is progressive enhancement - replacing the normal behavior of web pages with improved functionality to offer a better end-user experience, while still allowing users with less capable browsers to interact normally with it. This is accomplished by listening for events that would normally trigger some behavior, preventing that behavior, and executing a different function that gives a similar result. For example, many sites use a technique called PJAX, where a page may prevent links from navigating to another page and instead request the contents via AJAX, then replace the current page's contents with the response.

jQuery Events vs. DOM Events

Twitter Flight requires jQuery as a dependency, so all event binding is performed with the jQuery methods $(node).on() and $(node).off(). The basic usage of these methods simply adds or removes a function as an event listener to a collection of DOM elements. In addition to managing event listeners, jQuery also offers event delegation - binding an event listener function to a parent DOM element that is called when the triggering element (i.e. event.target) matches a given selector. This offers two main advantages over directly binding callback functions to the DOM elements that would trigger them. First, dynamic content inside the parent element does not need to manage rebinding all event listeners when the content changes; the event listeners remain attached to the parent element and will still trigger as normal when events occur on descendant elements. Second, memory usage is greatly improved since the number of bound callback functions is limited to the single event listener on the parent element.

jQuery also offers a convenience function $(node).trigger() to programmatically fire events from specific DOM elements. In addition to simulating native events such as click or mouseover, this method can also fire arbitrary events with the same bubbling behavior as native DOM events and - most importantly - with an additional data payload for event listeners to use. The combination of arbitrary non-native events with additional data make these three functions extremely powerful for communicating information with events, and is the core principle behind Twitter Flight's components.

Conclusion

Unlike many other libraries that introduce homegrown event systems, Flight works with the event system already available in the DOM. It relies heavily on the native behavior, so understanding exactly what that behavior is will help us avoid common pitfalls when writing our application. Flight filters the relevant part of events - attached data and bubbling - and hides the remainder to expose the most useful aspects of DOM events. Most importantly, understanding the parts of the DOM Event Model Flight utilizes will help us understand how Flight components should be written. Without further ado, let's start writing our application!

There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.