2

Basic question, but I have been pounding my head for a bit so thought id bring it here.

html looks like this (edit, fixed the closing quotes)

<span class='deleteimage-119'>delete</span>
<span class='deleteimage-120'>delete</span>
<span class='deleteimage-121'>delete</span>
<span class='deleteimage-122'>delete</span>
<span class='deleteimage-123'>delete</span>

javascript/jquery looks like this

iids = ['119','120','121','122','123'];
for (i=0; i<iids.length; i++) {
        place = iids[i];
        $(".deleteimage-" + place).click(function () {
                alert(place);
             });
    }

The click functionality gets attached to each individual span, but the alert after clicking just shows the last item in the array.

Mickey Slater
  • 374
  • 3
  • 15
  • 1
    You forgot the closing quote in your `span`s. – Lightness Races in Orbit Jun 10 '11 at 19:18
  • 5
    Possible duplicate of 10% of the Stackoverflow JavaScript questions :-) – Pointy Jun 10 '11 at 19:22
  • And they should be double quotes – morgar Jun 10 '11 at 19:22
  • (nothing personal, @Mickey Slater, that's why we're here :-) – Pointy Jun 10 '11 at 19:22
  • 1
    @morgar ?? single quotes are perfectly OK, even in strict XHTML, I think. – Pointy Jun 10 '11 at 19:23
  • my apologizes. that is just a typo in my question/example, the html presented is just there as an example, on my actual page there are many lines of code between each delete button but I did not want to put it all in here. – Mickey Slater Jun 10 '11 at 19:23
  • @Mickey Slater, welcome to closures. – zzzzBov Jun 10 '11 at 19:23
  • @morgar: Why's that? [Single quotes in HTML are perfectly valid.](http://stackoverflow.com/questions/273354/html-single-quotes-a-problem) – Lightness Races in Orbit Jun 10 '11 at 19:23
  • @pointy no offense taken. i did see lots of similar titled questions as i was adding mine.. but when i read them they didn't make sense to me ( although im sure the answer was somewhere in there ) – Mickey Slater Jun 10 '11 at 19:24
  • for clarification, jquery is attaching the click event to my span without a problem. the problem is that all clicks then alert, in this example, 123 – Mickey Slater Jun 10 '11 at 19:26
  • @Mickey: We understand the problem. :) – Lightness Races in Orbit Jun 10 '11 at 19:26
  • 1
    @Mickey - your main question has been answered, just a couple general questions/pointers: 1) why a specific class for each span? Do you have specific styling for each one? If not, then I think what you probably want to do is make those your id's, not classes. 2) do you really mean to step through just this list of id's, or are you actually looking to bind to the click of every span on the page? If the latter, you can dispense with the for loop, give each span the same class like 'deleteimage' and then bind them all in one line: $('.deleteimage').click(function() { alert $(this).attr('id')}); – Yardboy Jun 10 '11 at 19:26

5 Answers5

6

You have a scoping issue. By the time the callback fires, place has the last value from the loop.

You need to create a new variable for the closure; one variable per iteration, each of which will then be "caught" by the closure and used in the callback.

It would be nice if the solution were this:

var iids = ['119','120','121','122','123']; // don't forget `var` please
for (var i=0; i<iids.length; i++) {
   var place = iids[i]; // local variable?
   $(".deleteimage-" + place).click(function () {
       alert(place);
   });
}

Alas, Javascript has no block scope so there's still only one variable here called place, and it keeps getting updated as the loop runs.

So, you have to use a function instead:

var iids = ['119','120','121','122','123'];

function f(place) {
   // NOW `place` is a local variable.
   $(".deleteimage-" + place).click(function () {
       alert(place);
   });
}

for (var i=0; i<iids.length; i++) {
   f(iids[i]);
}

There are neater ways to employ this function approach using closures and bound variables, and the other answers cover those neater ways quite well. My answer has focused on explaining the issue.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • +1 for using this approach over putting a function inside the loop that returns another function... – Matt Jun 10 '11 at 19:24
  • @Pointy: You're not in the Scheme closet any more, my friend – Lightness Races in Orbit Jun 10 '11 at 19:28
  • thanks Tomalak! i had a feeling it was doing something like that but just wasn't sure how to get around it... hence my attempt at creating that local variable. oh and thanks for the reminders about adding var :) – Mickey Slater Jun 10 '11 at 19:30
0

The issue is with scopes and closure. In JS the scope is @ function level and not block level.

Try this:

var iids = ['119','120','121','122','123']; 
for (i=0; i<iids.length; i++) {         
    place = iids[i];
    var clickFn = function(a){
        return function(){
         alert(a);
        }
    }(place);
    $(".deleteimage-" + place).click(clickFn );     
} 
Chandu
  • 81,493
  • 19
  • 133
  • 134
0

This is because the click is occurring after the loop is completed, so you're alerting place = iids[iids.length - 1]. In order to achieve the result you're looking for you need to create a function closure and pass place in as a parameter:

iids = ['119', '120', '121', '122', '123'];
for (i = 0; i < iids.length; i++) {
    place = iids[i];
    (function(_place) {
        $(".deleteimage-" + _place).click(function() {
            alert(_place);
        });
    } (place));
}
mVChr
  • 49,587
  • 11
  • 107
  • 104
0

Inside the loop, you are binding the click event to those span, but the events are fired only after the loop is complete. So, it will always show the last value.

morgar
  • 2,339
  • 17
  • 16
0

As others have mentioned, you have a scoping issue. Unless all of those classes have a unique meaning, I'd move the place value to a data attribute and leave deleteimage as the class name. Something like:

<span class='deleteimage' data-place='119'>delete</span>
<span class='deleteimage' data-place='120'>delete</span>
<span class='deleteimage' data-place='121'>delete</span>
<span class='deleteimage' data-place='122'>delete</span>
<span class='deleteimage' data-place='123'>delete</span>

$(".deleteimage").click(function() {
    var place = $(this).data("place");
    alert(place);
});

If the place values aren't unique values, then this answer doesn't apply.

Chris
  • 4,393
  • 1
  • 27
  • 33
  • thanks, this is a pretty nice solution as currently i have a parent div around each span called 'deleteimage' that i apply the style too. redundant, which is why i like yours :) – Mickey Slater Jun 10 '11 at 19:33