views:

139

answers:

2

This jQuery 1.3.2 code adds an element to the page, registers a "click" event, then removes and reattaches the element:

var button = $('<button>Click me!</button>')
  .click(function(){ alert("Hello") })
  .appendTo('body');

$('body').html('');

button.appendTo('body');

The button appears on the page as expected, but clicking on it does nothing. I would like to know why the event handlers were removed from the object.

Note: I am aware of solutions such as jQuery.live() or clone(true) or using appendTo without a removal. What I'm looking for is an explanation, not a solution or workaround.

EDIT: I suppose this could be an arbitrary and counter-intuitive design decision of the DOM. An explanation like "Because that's the way section X of specification Y wants it to be" would be fine.

A: 

Why wouldn't it? You removed the element, it's handlers go with it. It's as simple as that.

It's like saying you bought a Honda Civic and replaced the radio (click handler) with a better one (fancy pants jQuery-ified handler :)), now you trade in the Civic for a better model (one with a sun-roof or something) and expect your new radio to be there still... Well, it could be if you just make sure to re-install it in the new one.

Maybe that's an odd analogy... the point is just that event handlers are quite literally on the element. If you remove it, the new one is just that: new, and so if you think about it it would be quite annoying to have handlers added earlier magically applied to new elements without your knowledge (indeed, as you pointed out, that's what live is for).

thenduks
There is no *new* element here: I'm inserting the removed-but-not-deleted element back, so I would expect it to come back with its handlers still attached...Or do you mean that `appendTo()` creates a new element? And if it does, why does the click handler remain after the *first* `appendTo` but not the *second* one?
Victor Nicollet
There is no such thing as 'removed but not deleted'.
thenduks
Change your code to create the button and add the handler and store _that_ in the variable. Then add it twice. jQuery is simply being nice and holding onto the handler for you, but if you use the return value of `appendTo` you are getting back not just the orphan jQuery object but the actual DOM element wrapped.
thenduks
Actually, the DOM Spec (http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001) does say "remove" and "delete" are different ("The thing that is deleted is not returned. The thing that is removed may be returned, when it makes sense to return it.") If `html('')` did delete the element, how could the second `appendTo()` even know that it was a button with a 'Click me!' label in the first place?
Victor Nicollet
Assigning the `button` variable before the `appendTo` yields the same result as assigning it after the `appendTo`. Also, doing `appendTo` twice without `html('')` in-between does not cause the event handler to disappear, even if I append to different elements.
Victor Nicollet
We're talking about jQuery here not the straight DOM methods they're talking about in the spec. I guess `$('body').html('')` is more agressive than I realized, but obviously you never actually do this... I've created elements before with handlers, put them in an array or object somewhere and re-added them lots of times maintaining their handlers. Often with delete buttons and the like for rows of data. You seem to have figured it out anyway, so, nevermind.
thenduks
+1  A: 

When you remove an element from the DOM using jQuery, all data (including event handlers) held by jQuery on that element will be destroyed. This is done to avoid memory-leaks.

This isn't a feature (or bug) of the DOM API. It's just jQuery.

J-P
You mean, the IE DOM-and-JS circular reference bug? I see. As a matter of curiosity, how does jQuery detect that the element was removed from the DOM? Is there an event for that?
Victor Nicollet
jQuery will only "clean" the data if you remove the element using one of jQuery's methods. If you just use `button[0].parentNode.removeChild(button[0])` then jQuery won't know about it.
J-P
Right. So, jQuery's `html()` also runs a removal on all children before setting the new content? Smart. And yet another reason not to use `innerHtml` :)
Victor Nicollet