Emerging Patterns in JavaScript Event Handling

By Marcello La Rocca

During the last few months the debate on the web about the best way to handle events has thrived. First, a few months ago, Google released the JsAction library; then, more recently, the Object.observe() method was introduced as part of the ECMAScript 7 specification (but already supported in Chrome 36 and Node.js Harmony).

Developers had already been taking sides on whether it is still “mandatory” to have all the logic confined to script files, or if it is acceptable or even preferable to inline selected parts of this logic into HTML. In this post, we will try to sort out this debate, going through the different patterns for error handling, and then weighting pros and cons of these alternatives.

The Facts

JsAction is a Google library for event delegation in JavaScript. It is based on Closure Library, and was introduced on Google maps a few years ago to overcome some browsers errors related to management of event listeners. JsAction aims to decouple events from the methods handling them, and to do so it moves part of the event handling logic to the HTML.

A general, recent trend has started that moves part of the logic not just to HTML files, but within DOM elements affected by that logic. This isn’t true just for event handling: a number of template-based frameworks (like Angular, Ractive, React) are emerging; they enforce the Model-View-Controller pattern in web application, and allow data-binding and reactive programming.

The introduction of the Object.observe() method in the next ECMAScript specification is another step in that direction, since it allows developers to natively apply the Publisher/Subscriber pattern to a whole new set of situations, and not just event handling. Declarative frameworks are already based on this logic, but the introduction of Object.observe() will help them gain an amazing improvement in performance.

The Story So Far

Since the introduction of JavaScript, the orthodox way to handle events has changed several times. Initially, if you wanted to add dynamic behavior to elements on your page, you only had one way: adding an attribute to the tag itself, and associate a snippet of JavaScript code with it. You could either write code inside the attribute value, or call one or more functions previously defined in the global scope.

For example, to change the background of your page to blue with a button click:

<button onclick="document.bgColor='lightblue'">Feel Blue</button>

It wasn’t long before the limitations and hazards of HTML on[event] attributes were discovered. As of November 2000, the addEventListener method was added to ECMAScript 3 specification as an alternative way to bind handlers to browser events. Previously, Microsoft had already added the attachEvent() method, but it took a while to catch on. While word spread on the Net in early 2000s, it was not until around 4 years after that the term unobtrusive JavaScript was coined.

The Netscape approach that in-lines event handlers had, indeed, some downsides that the event listener approach solved:

  • Mixing code and markup can make your code less readable and far less maintainable.

  • Global Scope Pollution: in-line code is defined in the global scope, and every function called in it must also be defined in the global scope.

  • It’s a weak spot for XSS injection: the attribute can contain any code that will be fed to the “evil” eval function without any control.

The introduction, in 2006, of the first widespread Ajax libraries, YUI and jQuery, pushed this new approach beyond any expectation, and they enforced good practices simply making them the most convenient choice for developers.

They also added to the event listeners approach:

  • Scalability: encapsulating an event handler into a function is DRY-compliant, since it allows to “prototype” and reassign the same logic to multiple handlers; jQuery CSS selectors added an easy and effective way to attach event handlers programmatically to set of nodes:
$(document).ready(function () {
  $('.clickable').click(function () {'lightblue';
    return false;
  • Debugging: with in-browser tools like FireBug and Chrome Developer Tools, debugging JavaScript became less of a nightmare, but in-lining code would frustrate it all.

Problems With the addEventListener Pattern

The event listener approach, though, raised some serious concerns:

  • Attaching listeners to objects in JavaScript can lead to closure leaks, if it’s not done properly. Closures are one of the most powerful language features of JavaScript, but they must be used with caution when intertwined with DOM elements. Closures keep a pointer to their enclosing scope. As a result, attaching a closure to a DOM element can create a circular reference and thus, a memory leak. This example from Google’s JavaScript Style Guide shows the right and wrong way to deal with it.

  • Internet Explorer had a rather problematic handling of garbage collection, especially when it came to events. Besides the well known mutual circular reference problem, in older versions of Microsoft’s browser, when a node was removed from the DOM its handlers weren’t garbage collected, and this caused memory leaks.

What’s JsAction Again?

This leads us right to JsAction. As mentioned at the beginning of this post, it is an event delegation library that allows mapping between events and handlers via their names, using a custom HTML attribute called jsaction, which will be directly handled by the library.

Each event handler is separately registered in one or more JavaScript files or inline scripts; they are associated with method names, and since the mapping between names and functions is taken care of by the library itself, there is no need to add them to global scope.

In summary, JsAction should provide a few advantages:
1. Work around memory leaks problems in some (older) browsers;
2. Reduce or avoid global scoping pollution;
3. Reduce coupling between events and handlers implementations;
4. Better performance and scalability, since it allows to set one event listener per page, and then routes itself the events to the proper handler;

To see an example of how it works, check JsAction’s GitHub page.

Truth to be told, the example code isn’t exactly easy to read, nor as simple as you would expect. Also, most of the properties above can be obtained with a few lines of JavaScript. Global scope pollution, for example, can be limited using the module and namespace patterns. Late loading can be as easily achieved by initially assigning stubs to the event handlers, then asynchronously load an external script with the real handlers and remapping the events on completion.

Implementing points 3 and 4 is a bit more complicated: we need to set a single handler for the whole page, set an attribute in the DOM elements stating which method will be used as handler, and create a “super-handler” method that routes the workflow to the appropriate method.

Once again, it may or may not be the right solution for your needs, depending on the characteristics of your project. Despite its many pros, it still has some weakness:

  • The library isn’t exactly lightweight.

  • It doesn’t look particularly intuitive to use, and the learning curve will probably be steep for beginners. The documentation is skinny, and that doesn’t help.

  • It can be hard to get started with it. Without a compiled version available, you are forced to download Closure compiler and Closure library.

Declarative Frameworks

So, JsAction might not be the definitive solution to event handling in JavaScript, and, as we saw, it has been been around for a while, although not as an open source project. And yet, after it was open-sourced, a lively debate started on the net between enthusiasts and critics. Besides the innate love for flames of the Internet generation, I believe that one of the main reasons is probably the fact that declarative frameworks, whose popularity is quickly rising, largely share the same design choice, with a higher degree of integration between presentation and logic and a return to in-line code not just for event handlers, but even for populating page elements with content.
Wait a minute, wasn’t mixing logic and presentation bad? Well, it is! We mentioned a few advantages of having your logic separated from presentation, easiness of debugging and clarity above all. But, sometimes, maintainability can be improved specifying the logic connected to an object next to the object itself.

Frameworks like RactiveJs, Angular, Ember, and React are not just meant to let you inject code in your views. They heavily use template based models for presentation to allow you binding event handlers, data and even presentation logic directly inside the DOM elements, and then specify the details of these logic in separate scripts. Basically, it’s the same schema used by JsAction to decouple event handlers names and handlers implementations. All in all, they rather increase separation between presentation and logic by enforcing the application of the MVC pattern to an higher degree, and at the same time they allow a very convenient use of templates.

These frameworks controls much more than event handling. They also allow data-binding, which starts to be important when you care about Model-View-Controller separation. They let you bind parts of the view to JavaScript objects, updating it every time the object behind it is modified. Moreover, they update views in particularly efficient ways, modifying only the smallest DOM nodes affected by the change, limiting page repainting, since that would be a bottleneck in most webapps.

To this end, Ractive and React use a virtual DOM – an abstract representation of the DOM that allows for very fast operations by minimizing the amount of DOM manipulation that needs to take place. They are very similar to each other, both focusing on reactive programming and visualization. While Angular is not just focused on the view part of MVC, it is a more complex framework that, at the same time, handles routing, connection to the server, etc.

All these frameworks support two-way binding, a convenient way to enforce consistency between values in the DOM and state in the application logic. Say, for example, that you need to display a list of items in your page. Suppose you want to use a traditional imperative paradigm. Then, you’d need to do something like this:

<!doctype html>
    <div id="container" class="container" >
    <script type="text/javascript" src="..."></script>

function createItemHTML (val) {
  return '<span class="">' + val + '</span>';

function displayList (container, items) {
  $.each(items, function (index, val) {
    var element = $('<div>');
    element.attr('id', 'div_' + index);

function editItem (container, itemId, itemValue) {
  var element = container.find('#' + itemId);
  if (element) {
displayList($('#container'), items);
editItem(container, id, newVal);

The code above makes use of some good patterns to avoid repetition, but still you can see that we are mixing logic and presentation, just the other way around.

Now, let’s see how you would do the same thing in Ractive:

<!doctype html>
    <div id="container" class="container" >
    <script src=""></script>
    <script src="logic.js"></script>
    <script id='listTemplate' type='text/ractive'>
        <div id="div_{{num}}" on-click="itemClick">
var ractive = new Ractive({
  el: 'container',
  template: '#listTemplate',
  data: {
    'items': items

    'itemClick': function (e) {
      //access e.node and e.context for both the DOM element 
      //  and the Ractive state associated with it


//Now update items with a new list
ractive.set('items', newItemsList);

That’s it! No need to write code to update your page. Ractive will take care of it for you. It is clearer, more maintainable, better designed, and more performant. We were even able to add event handlers to our items in a scalable way.


Object.observe() is a peek into the future, since it hasn’t even made it into ES6 specification – it has just been added to ES7. However, Google has already implemented it in Chrome 36, and the Observe-JS Polymer library will mimic support for it in every browser, exploiting native support when available.

This method allows you to asynchronously observe changes to objects and arrays. Observers will receive time-ordered sequences of change records describing the set of changes which took place in a set of observed objects. With Object.observe(), event-centered programming, otherwise known as reactive programming, is not restricted to the user interface anymore. For example, you can implement two-way data binding with language primitives – no need to install a framework like Ractive just for that.

Data-binding in Declarative Frameworks

One solution to provide data-binding is dirty-checking, (used by Angular). Any time data could have changed, the library has to go and check if it actually did, using either a digest cycle or a change cycle. Angular’s digest cycle identifies all expressions registered to be watched and checks if there are any change.

Another solution, used by Ember, Backbone, and Ractive, is employing container objects. The framework creates objects which hold the data. These objects have accessors to the data and so every time you set or get any property the framework can capture your action and internally broadcast it to all the subscribers. This solution works well and it is relatively performant in comparison to dirty-checking, with a good algorithmic behaviour, proportional to the number of things changed.

Performance Improvement

The new method added to the language allows us to observe an object, mutate properties, and see a report of what has changed. If you want to watch a plain JavaScript object, it is as easy as this:

// A model can be an object literal
var plainObject = {
  name: 'Counter',
  total: 0

// Define an observer method
function observer(changes){
  changes.forEach(function(change, i){
    console.log('what property changed? ' +;
    console.log('how did it change? ' + change.type);
    console.log('whats the current value? ' + change.object[]);
    console.log(change); // all changes

// Start watching the object
Object.observe(plainObject, observer);

At some point, you might decide that you don’t need to watch that object anymore:

Object.unobserve(plainObject, observer);


As mentioned above, native support for Object.observe() has only been added to Chrome 36 and to nodejs Harmony (enable it with the --harmony flag). Opera is also reported to be at work for shipping support to native data-binding in one of the next releases. In the meantime, waiting for other browsers to catch up, you can use Observe-JS Polymer library, in order to guarantee that your application will work even with older browsers versions as well.

As you can imagine even declarative frameworks have, on average, embraced this as an opportunity: Ember and Ractive are planning to release full support for Object.observe() asap, in the next releases; at Angular they have a more “long-term” approach, so they are working to add it in version 2 of the framework.


We have taken a long tour to review pros and cons of a few design choices, and taken a peek at the future of web development. Hopefully after reading through the whole post you are now at least aware of a variety of solutions and patterns that can help you dealing with event handling and data binding. When you face your next design challenge, keep in mind that there is not one single right solution for all problems.

References and Further Reading

  1. Crockford on JavaScript – Episode IV: The Metamorphosis of Ajax
  2. Google JavaScript Style Guide
  3. Javascript Closures
  4. JsAction Repo on Github
  5. The difference between Ractive and Angular
  6. The difference between Ractive and React
  7. Containers & Dependency in Ember.js
  8. Data-binding Revolutions with Object.observe(), by Addy Osmani
  • Paul

    Very insightful and the Object.observe( ) method will be the standard in a very short time I believe.

    • Marcello La Rocca

      Thanks :)
      I too believe that Object.observe() will be adopted very quickly and smoothly, and I guess it will have good repercussions on web apps performance

  • Rakhi Dhavale


    • Marcello La Rocca

      Thanks :)

  • Tim Ruffles

    I don’t think cycles are a problem with modern JS engines ( Therefore if the element is removed from the DOM both the closure and DOM element are available for GC as normal.

    • Marcello La Rocca

      Thanks for your feedback.
      Yes, if the GC uses coloring algorithms instead of reference counting, the odds are pretty good that mutual circular references won’t be a problem.
      However memory leaks are easier to make and more frequent than one could expect… and of course if you accidentally keep (elsewhere) a reference to the function, when you remove the element neither the function nor the element itself will be garbage collected.

      I think that, in the long term, it will repay you if you use some of the good practices like the one mentioned in google’s guidelines or here
      However I would never suggest to “avoid closures altogether” because they are the coolest and probably also the best language feature for JavaScript, but avoiding circular references, and nested or unnecessary closures – and always naming them – is probably still a good idea – it also has the non-trivial-at-all side benefit of making your code clearer and more maintanable.

  • chico

    > “All these frameworks support two-way binding”

    that’s not true. react has one-way binding

  • chico

    ReactLink is not part of React. React itself has one-way dataflow as its ideology.

  • chico

    more than that it is still one-way binding there. it just looks like two-way :)

    • Marcello La Rocca

      I think that it is well explained in the post I had linked.
      But, for sake of clarity:
      Technically yes, as reported in that post, React is based on a one-directional flow of control, and natively provide one-way binding.

      But it also says: “However, there are lots of applications that require you to read some data and flow it back into your program. […] In React, you would implement this by listening to a ‘change’ event, read from your data source (usually the DOM) and call setState() on one of your components.”

      Which is basically what you do in two-way binding [2-wb below] – you can have it, you just need to write some boilerplate code to get there. By the way, with Object.observe(), you’d get 2-wb almost for free, and most of those frameworks are basically going to use this pattern (with O.o()) to implement 2-wb efficiently.

      Reactlink is an addon to React that provides helpers, “syntactic sugar” that spares you typing boilerplate code to keep synchronization between DOM and state – as a matter of fact adding two-way binding to the framework.

      So technically you are right pointing out that React’s core doesn’t offer two-way binding shortcuts natively. It certainly wasn’t born with 2-wb in mind.
      On the other hand, however, they have realized how valuable a feature it was, and now it is supported with an add-on. You can download the framework version with the add-ons (react-with-addons.js) and spare writing 2-wb by hand.

      That’s why I’d say ReactLink is “kind of part of React” (it is an official add-on) and React “kind of have” 2-way-binding.



Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in JavaScript, once a week, for free.