The problem with the first example is obscured by the scope of the variables. In JS all variables have function scope so rewriting the code to show that more explicitly would result in:
var i,data,obj;
for(i = 0; i < 5; i++) {
data = { val: i * 2 };
obj = $('<li>Item</li>');
$("ul").append(obj);
obj.click(function() {
alert(data.val);
});
}
Now it's more obvious that data
inside the loop is just one variable and not a new for each iteration (as in would be in Java, C# and the like). The anonymous function passed as the click handler uses this variable, it closes over it. The result of that is that the current value of the variable when the function is executed is used, not the value the variable had when the function is assigned as a click handler.
To get around this you can either attach the data to the element itself.
In your code you do obj.data = ...
however that attaches the value to the selection and not the element. Next time you select the same element you have a new object without the data.property. Instead you can use the jQuery .data()
method.
obj.data('key',data);
obj.click(function() {
alert($(this).data('key').val);
});
or if you would want to embed the value into the function you can do that with a self executing function. When you pass the variable as an argument to a function you are using the current value instead of closing over the variable.
var i,
data,
obj,
createClick = function(data) {
return function(){
alert(data.val);
};
};
for(i = 0; i < 5; i++) {
data = { val: i * 2 };
obj = $('<li>Item</li>');
$("ul").append(obj);
obj.click(createClick(data));
}
Note
As an aside you should get into the habit of putting {
at the end of a line not the begining. Semi colon insertion can have some unexpected results if {
is on the next line.
E.g
return
{
val : 1
}
might return undefined
because a semi colon might be inserted after return
ignoring the object literal