views:

1919

answers:

4

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?

+5  A: 

jQuery 1.3+ has a new live() function that can set up event handlers for elements that don't exist yet .. check it out

Scott Evernden
A: 

You have to wait for the element to be added to the page, then add the event handler then.

There is no easy way to say "add this to all elements of this type, now and in the future".

It is possible to have a timer periodically check the page for new elements, applying a queue of events (or other properties) to them as they appear, all behind the scenes. This can be abstracted out and re-used, for example Jquery can do that sort of thing.

thomasrutter
+10  A: 

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.

J-P
Yes, the ID is holding information. For each of these different TDs, I want the onclick event to call the same function, but with different parameters in the call. My hack was to stuff the parameters into a single string and let the method I'm calling parse it for the 5 different numbers it needs.
+1  A: 

As JimmyP pointed out, your problem can easily be solved using event bubbling. You might consider writing a wrapper function to work around browser inconsistencies - my own version can be found here and would be used like this:

capture('click', '#element-id', function(event) {
    // `this` will be the originating element
    // return `false` to prevent default action
});
Christoph