Let's Make a Framework: Events

2010-04-22 00:00:00 +0100 by Alex R. Young

Welcome to part 9 of Let's Make a Framework, the ongoing series about
building a JavaScript framework. This part introduces events.

If you haven't been following along, these articles are tagged with
lmaf. The project we're creating is called Turing and is available on GitHub:

This part will look at how events work, event handler implementations in
various frameworks, and event handler API designs. I'll select an API
design at the end of the article and implement it for next week's


Events and JavaScript are closely related -- can you imagine writing
scripts for web pages that don't respond to user interaction? That means
that as soon as JavaScript appeared, events did. Early event handlers
were written inline, like this:

You've probably seen this type of inline event handler before. This came
from Netscape originally, and due to the popularity of early versions of
Netscape, Microsoft implemented a compatible system.

This can be written in JavaScript like this:

// assume 'element' is the previous link minus the onclick
element.onclick = function() { alert('Hello World!'); };

Accessing the Event

An event handler can access an event like this:

function handler(event) {
    if (!event) var event = window.event;

window.event is a Microsoft property for the last event. I
always felt like this was dangerous and could lead to to a clobbered
value, but as JavaScript is single-threaded it's safe enough that most
frameworks depend on it.

jQuery does it like this:

handle: function( event ) {
  var all, handlers, namespaces, namespace_sort = [], namespace_re, events, args = jQuery.makeArray( arguments );
  event = args[0] = jQuery.event.fix( event || window.event );

Stopping Events

I used to get default actions and bubbling confused, because I thought
stopping an event just meant everything stopped. These two things are

Default Action

Returning false from an event handler prevents the default

element.onclick = function() { alert('Hello World!'); return false; };

Now the link won't be followed.

Capturing and Bubbling

Attaching an onClick to two elements, where one is the
ancestor of the other, makes it difficult to tell which element should
take precedence. Different browsers do it different ways. However,
fortunately it's very unusual to actually care about this -- in most
cases we just need to stop the event.

function handler(event) {
    if (!event) var event = window.event;
    event.cancelBubble = true;
    if (event.stopPropagation) event.stopPropagation();

As far as I know, only IE uses cancelBubble, but it's safe
to set it in browsers that don't use it. stopPropagation is
used by most browsers.

jQuery's implementation in
event.js is similar to the above, and most frameworks are broadly similar.

Multiple Handlers

If events were this simple we'd barely need frameworks to help us. One
of the things that makes things less simple is attaching multiple

element.onclick = function() { alert('Hello World!'); return false; };
element.onclick = function() { alert('This was the best example I could think of'; return false; };

This example overwrites the onClick handler, rather than
appending another one.

The solution isn't as simple as wrapping functions within functions,
because people might want to remove event handlers in the future.

Framework APIs

The job of an event framework is to make all of these things easy and
cross-browser. Most do the following:


Interestingly, jQuery's event
is to make the event object behave like the W3C standards. The reason
for this is that Microsoft failed to implement a compatible API.

jQuery wraps methods into its internal DOM list objects, so the API
feels very easy to use:

$('a').click(function(event) {
  alert('A link has been clicked');

This adds the click handler to every link.


Prototype's event handling is closely linked to its core DOM Element class. Events are
registered like this:

$('id').observe('click', function(event) {
  var element = Event.element(event);


Glow's event handling is found in
glow.events. Events are registered with addListener:

glow.events.addListener('a', 'click', function(event) {
  alert('Hello World!');

Glow's jQuery influence is evident here.


Rather than ironing out the problems in browser W3C event handling
supporting, dojo uses a class-method-based system like Prototype, but
with a different approach. Dojo's API is based around connections
between functions. Registering a handler looks like this:

dojo.connect(dojo.byId('a#hello'), 'onclick', function(event) {
  alert('Hello World!');

Notice that the event names aren't mapped. Like the other frameworks,
the event object is normalised.


Out of all these frameworks, jQuery's event handling is the most
fascinating. It makes a familiar W3C event system available to all
browsers, carefully namespaced, and completely takes over event bubbling
to achieve this. This is partially because Microsoft's API has major
problems with bubbling.

Building something in between jQuery and Glow is suitable for Turing --
I don't want to worry about bubbling too much, but we will need a system
that will cope with multiple handlers on the same event and the removal
and firing of handlers.

Next week I'll start building the event handling code, building with the
following goals in mind:

If you want to read more about events, browser wars, and Microsoft's
frustrating inability to make life easy, Introduction to
on QuirksMode
covers the history and basics, and branches out into an entire series.