2
votes

I have a problem when converting a static html table to one which is generated by JavaScript. Previously, my js/jQuery code set click handlers on the data elements in the table as follows:

$(function() {
   $('my_table td').click(function(e) {
   ...handler code
});

This works fine, but I've had to change my table so that the contents are dynamically generated in JavaScript:

// 'table_contents' is "<table><tbody>...</tbody></table>"
$('#my_table').html(table_contents);

When I do this, I lose the click handlers. I suppose this isn't surprising, since I'm just deleting the old html and replacing it with new html. However, I don't know how to handle this properly. Can I just give my anonymous function a name and call it whenever I change the html, or do I have to do something else, like explicitly adding an event listener to each new td element? And do I have to do anything to clean up after replacing the old html, like freeing the old handler/listener? Does js have memory/resource leaks that I have to fix manually?

Thanks.

EDIT

Sorry to be dumb about this, but I can't get the suggested on/delegate solutions to work. My test html is:

<div id="date_test">
<table><tbody><td>42</td></tbody></table>
</div>

I've tried to respond to a click on td '42' in 3 different ways:

$("#date_test").on("click", "td", function() {
   alert($(this).text());
});

$('#date_test').delegate('td', 'click', function() {
   alert($(this).text());
});

$(function() {
   $('#date_test td').click(function() {
      alert($(this).text());
   })
})

Of these, only the third one works. Note that this is completely static code; it's 3 different cut-down test cases. for the first 2 cases, the code is never executed; I can put in a syntax error instead of the alert, and it makes no difference. Any idea what I'm doing wrong? I'm on jQuery 1.7. Thanks.

4
Just noticed your edit. I've updated my answer.James Allardice

4 Answers

7
votes

You can use the on method to delegate the event handler to an ancestor element. Assuming #my_table exists in the DOM at the point the following code is executed:

$("#my_table").on("click", "td", function() {
    //Do stuff
});

The on method is only available in jQuery 1.7+. If you are using an older version you can use delegate instead.

This works because DOM events bubble from the element on which they originated up through the DOM. The events are captured on an ancestor, and if the origin matches the selector, the event handler is triggered.

This not only offers the benefit of handling events that originate on elements that were not part of the DOM originally, it can also improve performance because it requires less event handlers (if you bind an event handler to every td, and there are lots of td elements, that's a lot of event handlers - using event delegation means there's only one).

Update (see edit on question)

The reason your third example works is that you have wrapped the code in a ready event handler. $(function() { ... }); is shorthand for $(document).ready(function() { ... });.

This is necessary because otherwise the #date_test element to which you want to bind the event handler will not exist in the DOM. If you are performing any DOM manipulation, always put the code in a ready event handler.

You can place either the on version or the delegate version inside the ready event handler function and it should work.

1
votes

I recently had the same issue. It turns out elements that come into the page after the JS is loaded will not have the events attached to them. I solved this very easily by using delegation http://api.jquery.com/delegate/. It propogates the event up through the parent elements until they get caught and executed. The trick is to make sure you delegate the event to an element that will always be there when you first load the js, so any new elements added inside that container will have their events captured.

$('#container').delegate('#my_table td', 'click' (function(e) {...
0
votes

Depending on the version of jquery you are using you can try using the jquery .on()

http://api.jquery.com/on/

if you are on older versions then the .delegate() will work for you:

http://api.jquery.com/delegate/

0
votes

You can use the on() method http://api.jquery.com/on/

$("#my_table td").on("click", function(event){
    alert($(this).text());
});