0

I'm calling on click with the following:

$("img").on("click", "td img", function().....

It works the first time I click but then stops working. The images are all in the div id = ocean. If I change the call to:

$("#ocean").on("click", "td img", function()....

it works fine. Can someone explain why the first case only works once?

I have the following code:

for (var dolphin in ocean_array)  
{
    if (ocean_array[dolphin] == null)
    {
        html += "<td></td>";
    } 
    else
    {        
        html += "<td><img data-index='" + dolphin +"' src=" + ocean_array[dolphin].picture + " width=150 height = 100></td>";        
    }
}

html += "<td col-width='6'></td>"

// end HTML string
html += "</tr></table>";

// print out HTML string
$("#ocean").html(html);
JAAulde
  • 19,250
  • 5
  • 52
  • 63
  • 7
    You have img elements in your img elements ? The first one doesn't seem to make sense. – Denys Séguret Apr 24 '14 at 12:44
  • 1
    You have table cell and image element in your img element? The first example seems weird to say the least. Show your html but most likely your first example is not the right way to do what you desire – Huangism Apr 24 '14 at 12:45
  • 1
    You need dynamic delegation because you dynamically create elements. Refer to http://stackoverflow.com/questions/1525664/jquery-how-to-bind-onclick-event-to-dynamically-added-html-element/17086311#17086311 – MrCode Apr 24 '14 at 12:59
  • @dystroy That doesn't imply that there are images within images. Just that it will only work for images within tds. The query doesn't start from the selected element, it just runs a `if ($(target).is('td img')`. Not that it makes a lot of sense – Ruan Mendes Apr 24 '14 at 13:00

2 Answers2

1

As to why the first works until after the first click, if the code in the function you're setting as the click handler replaces the DOM elements you originally bound the click to (the images jQuery found when you ran $('img').on(...), then the elements you bound the click to are gone and new ones have been put into the DOM. So you originally run the code, and the click is bound and works. But then you click, and cause the replacement of the elements which had click handlers, so no further clicking will work.

The latter works because you're delegating the click handling to the parent container, which container you are not replacing. You see, jQuery's .on method can be called a number of ways, changing what it binds to and what elements it will react to.

When called as you have, with three parameters, it says that whenever an element in the collection receives a click event from something which matches the selector in the second parameter, the function given in the third parameter will run. Given your second (and working) example:

$("#ocean").on("click", "td img", function()

This says that when the element with ID of ocean receives a click event, it should check to see if it originated from an image which is in a table cell.

To explain a little more clearly, event bubbling in jQuery happens when any DOM event takes place. If I click an image, any handlers for that image are run, then the event is passed up to the image's parent. Again, any bound handlers are run, and it's passed up again. This continues all the way up to DOM to the document element. (Natively, some browsers don't do this quite the same, but jQuery levels the playing field)

In the example I copied, you're telling jQuery to listen for click events which started at, or have bubbled up to, the DIV with ID ocean. When it receives such an event, it is to check that the event originated at an image element which lives within a TD. If it does, your function runs.

Even if your function replaces the contents inside of the DIV with ID of ocean, the delegated click remains bound to ocean, so bubbled events continue to be caught and handled.

Now, let's go back to your "working once" example--it has some oddities besides the problem you're noticing:

$("img").on("click", "td img", function().....

This says jQuery should listen for events originating at, or bubbling up to, an image element that's in a table cell. Do you see the problem with this? Images cannot have descendent elements, so such a delegation doesn't make a lot of sense.

What this will do is is bind a click to all img elements in the DOM. When clicked, the event first arrives at the clicked element, an image, and the check for td img is run. If it matches (if the clicked image was, in fact, inside of a table cell), the handler runs. If images could have descendants, this would also work for any images which live under the clicked image and inside of a TD. This cannot happen, though. So in practice, your code will only bind to images, and run the handler for those which live in a TD. This is identical to:

$("td img").on("click" function().....

This line is better to run as

  1. It only finds and binds to images which live in a TD
  2. No further selector matching has to be done to figure out if the handler should run.

Note, though, that this would still suffer the problem of working once, then stopping, if you replace the images you bound the click to. Thus your delegation to ocean (your second line of code in the original post) is what you need.

JAAulde
  • 19,250
  • 5
  • 52
  • 63
  • that makes sense but why then does the second query work the first time I click? –  Apr 24 '14 at 13:24
  • I apologize for missing the crux of your actual question. I've reformulated my answer for you. Further edits may be coming as I continue to re-read what I provided to you. – JAAulde Apr 24 '14 at 15:02
0

jQuery's .on() accepts a second parameter as a selector for one of the descendants of the original selector.

$("parentElement").on("click", "childElement", function()...

Alternatively, if you're not looking to exploit event propagation, you could use

$("td img").on("click", function()...

It may however be more efficient to use the first method if you have a large number of images you are trying to bind the function to. If you don't fully understand the idea of event bubbling that article is definitely worth reading.

Henry Marshall
  • 850
  • 2
  • 8
  • 21
  • What? The first one is more efficient? The first is not using event delegation properly... – Ruan Mendes Apr 24 '14 at 12:58
  • I tried: $("td img").on("click", function()... as you suggested. It works once but then will not work the next time I click on an image –  Apr 24 '14 at 13:27