How do I register a Javascript event handler to an element that hasn't been added to the page yet

Randy picture Randy · Mar 8, 2009 · Viewed 7.8k times · Source

I'm trying to build a greasemonkey script which will dynamically create tables of data based on user interaction with... other dynamically created tables of data. My problem is that I'm having to make two passes every time I create a table: one to create the table, and another to go grab all of the objects in the table I want to add event handlers to (by id) and add the various event handlers to them.

If I attempt to, say, add an onClick event to a table td before I've created the table and inserted it into the HTML, I get a "component is not available" exception.

This is incredibly cumbersome, because I either have to maintain, separately, a list of the ids and what I should do to those elements when I make my second pass to add the handlers, or develop a naming convention by which I know, based on the id, what I should do with the element.

There HAS to be a better way to do this. I just haven't figured it out yet. Anyone have any ideas?

Answer

James picture James · Mar 8, 2009

Firstly, I'd love to know why you need a different ID for every single TD. Is the ID holding important information, such as an index? In this situation it might be better creating each TD within a loop. Also, obviously you can't attach an event handler to a DOM element which doesn't exist! It doesn't have to be injected into the DOM but it DOES have to exist in some capacity.

jQuery's live() isn't a magical mystery, it just uses event delegation, so it attaches the event to a parent element, such as the table and then decides what happens dependent on the target of the click. Here's a rudimentary example. I register a handler to the 'body' element, and then I test each time to see what the target is, if it's a TD element I doSomething() ->

document.body.onclick = function(e) {

    var realTarget = e ? e.target : window.event.srcElement;

    if ( realTarget.nodeName.toLowerCase() === 'td' ) {
        doSomething();
    }

};

Event delegation relies on something called event bubbling (or "propogation") which is the way in which modern browsers implement the event model. Each event, when triggered will travel upwards through the DOM until it can go no further. So if you click on an anchor within a paragraph the anchor's 'click' event will fire and THEN the paragraph's 'click' event will fire etc. etc.