Broken Events

So, you've created your DOM element, hooked up your events, and…

What now?

Events that don't fire

A question that has often come up is, "Why doesn't my event fire?" or "My event fires once, and then stops!". More often than not, this has to do with use of the ubiquitous "innerHTML" property. Consider the following:

var d = new Element("div", { id: "foo" }).update("<span id='inner'>test</span>");

Simple enough. We're creating a div, with a span inside it, and hooking into the click event of the span (using Function.defer to give the browser a moment to process the HTML). Works like a charm. Now, say you create an AJAX request, and get a new value to replace "test". Many times, the code to insert your new element would simply be:

$('foo').innerHTML = "<span id='inner'>test2</span>";

Cool! That works perfectly. But, now clicking on your "inner" element doesn't work! Oh no!

This is because the event hooked to the element with the id "inner" is hooked to THAT INSTANCE of the DOM element. By removing the DOM element (via .innerHTML), you trash the reference to the event as well, and your nice alert will never be shown again.

To avoid this issue, you have to either:

  1. Remove your handler from the old element, update the content, and then hook up your handler to the new version of the element, or
  2. Only update the content of the element

In our example above, we'd probably Option 2: Just update the content of the 'inner' span. But in other situations, Option 1 is sometimes the better choice.

Events that won't stop firing

Sometimes it seems like you can't unhook your event handler, even when you're calling Event.stopObserving. Usually, in these situations your code looks something like this:

Hooking up the handler:

$('myelement').observe('click', this.clickHandler.bind(this));

Unhooking the handler:
$('myelement').stopObserving('click', this.clickHandler.bind(this)); // <= DOESN'T WORK

If you pass all three parameters to Event.stopObserving, the handler will only be removed if it's the same handler that was passed to Event.observe. In the example above, it isn't: Function.bind creates a new function whenever it's called, which is a wrapper for the original function. So the two calls to Function.bind above result in two distinct functions; and so Event.stopObserving can't unhook the old handler.

There are a couple of ways you can fix this. The most obvious is to make sure that you're unhooking the handler you hooked, by keeping a reference to it:

this.boundClickHandler = this.clickHandler.bind(this);
$('myelement').observe('click', this.boundClickHandler);

Unhooking the handler:
$('myelement').stopObserving('click', this.boundClickHandler); // <= WORKS

Alternately, if you know it's okay to do so, you can remove all click handlers from the element by just leaving the handler parameter off the call, or even all handlers by leaving off both the event name and the handler parameters. Details over in the Unhooking Events Tip.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License