0

I have a whole bunch of similar list items that I want to attach mousedown() functions to. So what I would like to do is replace this:

$('#controls li[id=d1]').mousedown(function(){
    console.log('d1');
});
$('#controls li[id=d2]').mousedown(function(){
    console.log('d2');
});

with this:

var loopvar;
for (loopvar = 1; loopvar <= 2; loopvar++) {
    $('#controls li[id=d' + loopvar + ']').mousedown(function(){
        console.log('d' + loopvar);
    });
}

(This is a simplified example - I actually have lots of li's to handle.) But when I click on an li, I always get d3 on the console. The value of loopvar is 3 when the loop ends, so it looks like that is what is happening. So how can I attach the functions using a loop?

PSL
  • 123,204
  • 21
  • 253
  • 243
Jon
  • 407
  • 1
  • 3
  • 11
  • 1
    You've just meet a thing called closure. One of the most powerful yet dangerous (if you don't know how to work with it) features of JavaScript. – Aurelio De Rosa Sep 11 '13 at 00:14
  • Welcome to JavaScript Closures http://stackoverflow.com/questions/111102/how-do-javascript-closures-work – fujy Sep 11 '13 at 00:14
  • 1
    In this case you can just use console.log(this.id) – bfavaretto Sep 11 '13 at 00:16
  • possible duplicate of [Javascript closure inside loops - simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – bfavaretto Sep 11 '13 at 00:16
  • I have decades of C/C++ experience and _weeks_ of JavaScript experience! So closure is a new concept for me. Thanks for the pointers. – Jon Sep 11 '13 at 01:12

3 Answers3

3

Try this way:

var loopvar;
for (loopvar = 1; loopvar <= 3; loopvar++) {
    $('#controls li[id=d' + loopvar + ']').mousedown((function(loopvar){
        return function(e){
            console.log(e);
           console.log('d' + loopvar);
        }
    })(loopvar)); //You create a closure locking in the loop iteration variable
}

But ideal scenario you would just need to bind an event to the a selector either usind a common class name or using an id starts with selector. Fiddle

With startswith selector:

$('#controls li[id^=d]').mousedown(function(e){
        console.log(this.id); //here this represents the element you clicked on.
    }
);

Fiddle

Also do remember that id starts with selector is an attribute selector and would be slower compared to a class selector

PSL
  • 123,204
  • 21
  • 253
  • 243
1
  1. You should never ever do that.
  2. you should use classes instead of ids.

To use IDs anyway, try :

$('#controls li[id^="d"]').mousedown(function(){
    console.log($(this).attr('id'));
});

To use a loop, you have to use a closure to pass the var, however it's bad to assign callbacks like this, use the method above.

for (var i = 0; i < 3; ++i) {
    (function(loopvar) {
        $('#controls li[id=d' + loopvar + ']').mousedown(function(){
            console.log('d' + loopvar);
        });
    })(i);
}
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • 1
    Thanks for the idea of the jquery selector and using a class instead of id's. Good stuff. I'm a C programmer, so I immediately jump to a `for()` loop for this kind of stuff. – Jon Sep 11 '13 at 01:10
  • Welcome to the javascript world, it's a wonderful world once you get the hang of it. – OneOfOne Sep 11 '13 at 10:20
0

If the example you've written is very similar to your actual code, I'd solve it like this:

$('#controls').find('li[id]').mousedown(function(){
    console.log(this.getAttribute('id'));
    // If you want to use jQuery...
    // console.log($(this).attr('id')); 
});

Note that $('#controls').find('li[id]') is slightly faster than $('#controls li[id]')

Aurelio De Rosa
  • 21,856
  • 8
  • 48
  • 71