Jay Sun
Jay Sun

Reputation: 1601

Adding click functions to DOM elements dynamically

In my test code, I have a simple div that I'm using as a container for 5 loop created div elements. I try adding a click function to all 5 div elements, but only the last one is given a click function.

<div id="testbed"></div>

<script type="text/javascript">
$(document).ready(function () {
    for (i = 0; i < 5; i++) {
        $("#testbed").html($("#testbed").html() + "<div id='" + i + "'>Hello!</div>");
        $("#" + i).click(function () {
            alert(i);
        });
    }
});
</script>

Interestingly enough, instead of alerting 4, it alerts 5. I don't know why it's only applying the click function to the last div element and not the first 4.

Upvotes: 1

Views: 139

Answers (4)

zatatatata
zatatatata

Reputation: 4821

The following is called a javascript closure. At the moment of binding, you run the anonymous function that returns another function that does alert(i) when the event is fired. The first anonymous function "wraps" creates another level of variable scope for the i, so when the original i is incremented later on, the i inside the anonymous function remains untouched. Nifty little trick.

<div id="testbed"></div>

<script type="text/javascript">
$(document).ready(function () {
    for (i = 0; i < 5; i++) {
        $("#testbed").html($("#testbed").html() + "<div id='" + i + "'>Hello!</div>");
        $("#" + i).click(function (i) {
            return function(evt) {
                // inside here, you have access to the event object too
                alert(i);
            };
        }(i));
    }
});
</script>

More information at javascript closures

Btw if you wish to add click events to all divs, you can't replace them with .html() in every loop, use .append() instead, like so:

<div id="testbed"></div>

<script type="text/javascript">
$(document).ready(function () {
    for (i = 0; i < 5; i++) {
        $("#testbed").append("<div id='" + i + "'>Hello!</div>");
        $("#" + i).click(function (i) {
            return function(evt) {
                // inside here, you have access to the event object too
                alert(i);
            };
        }(i));
    }
});
</script>

Upvotes: 0

Simon
Simon

Reputation: 2830

You need to add the click event to the object.

    $(function(){

      $('#testbed').empty();       

     for (i = 0; i < 5; i++) {
          $('<div />').text('Hello').attr('id', i).click(function(){
              alert($(this).attr('id'));
          }).appendTo('#testbed');
       }

    });

Upvotes: 0

Matthew D
Matthew D

Reputation: 131

You could also do something to this effect:

$.each([1, 2, 3, 4, 5], function(index, value) { 
     $("#testbed").append("<div id='" + index + "'>Hello!</div>");
     $("#" + index).click(function () {
         alert(index);
     });
 });

Upvotes: 0

SLaks
SLaks

Reputation: 887453

All of your click handlers are sharing the same i variable.
Since, after the loop, i is 5, they all say 5.

You need to create each handler in a separate function that takes i as a parameter, so that each handler will get its own i.

For example:

function buildElement(i) {
    $("#testbed").html($("#testbed").html() + "<div id='" + i + "'>Hello!</div>");
    $("#" + i).click(function () {
        alert(i);
    });
}


for (i = 0; i < 5; i++) {
    buildElement(i);
}

Upvotes: 2

Related Questions