From Inline Events to addEventListener: a Brief Overview of JavaScript Events

| Comments

What follows is a brief overview of event handlers in JavaScript. It’s not meant to be a thorough overview; the aim is to provide a beginner’s look at each event model, so we can understand when we might choose one over another.

It’s now pretty standard to keep document content (markup) and document presentation (CSS) in separate files, but this wasn’t always the case. Prior to the rise of CSS, designers and coders relied on less elegant solutions, e.g., contorting content into place using tables-inside-tables-inside-tables. For web browsers, finding content was like hunting for the smallest matryoshka doll.

It makes sense to organise like with like, so I was surprised to learn you can also muddy content with JavaScript behaviour: The original (and out-of-date) way to register an event handler - a function that’s executed when an event occurs - is to write it inline as an attribute of the HTML element, e.g.:

1
2
3
// As an HTML attribute
<input type="button" value="Hit Me"
  onclick="alert('This feels wrong!');">

This string of JavaScript code - written directly into the HTML tag above - adds an onclick property to a button, so an alert pops up when the button is clicked. This is a simple way to get interactivity into a web page, and web browsers still support this mix of content and behaviour just fine. But should we use it?

If you have only a small website, and you’re only binding to a handful of events, then maintaining things won’t present too much of a problem. That’s the argument for JavaScript in your markup. Now, here are two arguments against using this technique:

  1. It relies on old tricks.
  2. Keeping code in one place - rather than littering content with little bits of it - makes it easier to maintain and update.

Keeping JavaScript out of your markup is especially important if a site is large. Most programmers have better things to do than go looking for code hardwired into the content. And new and returning (perhaps after maternity leave, or an extended caravanning holiday) programmers need to get up-to-speed swiftly, a task made easier if the narrative of the program tells the full story, no pages bits missing. Here’s our JavaScript again, but this time it’s not riding an HTML element:

1
2
3
4
5
6
7
// Take Two - Click event on a button element
window.onload = function() {
    var button2 = document.getElementById("button2");
    button2.onclick = function() {
    alert("This feels better!");
  };
};

Otherwise, not much else has changed: We’re still setting an onclick property to a button.

Of course, things do change - we advance quickly into the future - and we want our sites and applications to keep up. Along the way, things get more sophisticated and demands change, so stay awake! One thing worth knowing about today’s premier event model - the W3C standard event model - is a single event can trigger multiple responses / event handlers, e.g.:

1
2
3
4
5
6
7
8
9
10
11
12
// NB Won't work for older versions of Explorer (pre-IE9)
window.onload = function() {
  var button3 = document.getElementById("button3");
  
  button3.addEventListener("click", function() {
      alert("I'm thinking...");
      }, false);
      
  button3.addEventListener("click", function() {
      alert("I like it!");
      }, false);
};

You can achieve the same double alert without addEventListener, but in this case one response / event handler must do it all, e.g.:

1
2
3
4
5
var button = document.getElementById("button");
button.onclick = function() {
  alert("I'm thinking...");
  alert("I like it!");
};

It follows that addEventListener allows you to bind a handler to an event from multiple places in your web app (without overwriting handlers set elsewhere). A web app will often have multiple JavaScript programs running at the same time; programmers working within a particular program can fearlessly add event handlers without consulting other programs.

An addEventListener function must take three parameters: event type (full list here), event handler, and finally a boolean value (almost always false). Really, really modern browsers don’t require the third parameter - if it’s not passed it’s default is false - but you should include it for the benefit of the masses. I’ll write a post about this final parameter next, since it concerns bubbling - a simple feature with big implications.

IE9 supports addEventListener, but we include the similar method attachEvent for older versions of Explorer (IE5 and later):

1
2
3
4
5
6
7
8
9
10
11
12
window.onload = function() {
  var button4 = document.getElementById("button4");
  var handlerA = function() { alert("I'm groovy!"); };
  var handlerB = function() { alert("I wish I was groovy!"); };
  
  if(button4.addEventListener) {
      button4.addEventListener("click", handlerA, false);    
  } else if(button4.attachEvent) {
      // NB Takes two parameters
      button4.attachEvent("onclick", handlerB);             
  }
};

A quick note about the final example: We’ve declared our handler functions first, passing them by reference. From the code I’ve seen, this seems to be a common practice, to help keep code readable when more is going on.

Comments