0

I have a problem with binding functions from an array to some elements, but i get this error after i click on any element with class "class".

Uncaught TypeError: Property '4' of object function (){alert(1)},function (){alert(2)},function (){alert(3)},function (){alert(4)} is not a function

var c = [
    function(){alert(1)},
    function(){alert(2)},
    function(){alert(3)},
    function(){alert(4)}
];

function test(b){
    for(var i = 0; i < b.length; i++){
        $('.class').eq(i).bind('click', function(){
            b[i]();
        });
    }
}

test(c);

I think that the variable i keep its last value.

any solution or explanation will be really appreciated. Thanks in advance.

Charlino
  • 15,802
  • 3
  • 58
  • 74
misoukrane
  • 304
  • 2
  • 3
  • 4
    That's not hoisting. That's scope. – Bergi Feb 26 '13 at 20:10
  • 1
    Try this: `$('.class').eq(i).bind('click', b[i]);` – gen_Eric Feb 26 '13 at 20:11
  • Your array only has 4 indexes (0,1,2,3) since your error is the 5th index is undefined, that means your loop must be going one too far, though i don't immediatly see why. Outside fo that though, you have a scope problem with your index. your click event b[i]() will always call the last function in the array. – Kevin B Feb 26 '13 at 20:12
  • @KevinB: After the loop is done, `i` will be `4`. All the functions "close" around the same `i`, so they all will say `b[4]` doesn't exist. – gen_Eric Feb 26 '13 at 20:13
  • 1
    @RocketHazmat Ah, so at the end of the loop, i is incremented one more time to 4, at which point it fails the test and exits, but it's still 4, causing it to fail inside the click event. Fixing the scope issue would solve that. – Kevin B Feb 26 '13 at 20:14

3 Answers3

2

You need a closure - in js variable scope is determined by functions. With jQuery, just use .each:

$('.class').each(function(i) {
     $(this).on('click', function(){
         alert(i+1);
     });
});

However, if you really need to have an array of distinct functions you just can bind the functions themselves directly as handlers:

var elems = $('.class');
for (var i=0;i<b.length;i++) {
    elems.eq(i).on('click', b[i]);
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

Your function array only has 4 items, so b[4] is undefined. Instead of referencing b inside your click handler, make it the click handler itself:

elems.eq(i).on('click', b[i]);

By doing this the value of i will be examined each iteration through the loop, instead of when the element is clicked (at which point its value should always be 4).

Madbreaks
  • 19,094
  • 7
  • 58
  • 72
1

The problem is, in your loop the functions you are setting as click handlers are all sharing the same i value. After the loop, that i value will be 4, so each function will try to call b[4], which doesn't exist.

You need to create a new scope for each of the functions, so that the i values are all different.

for(var i = 0; i < b.length; i++){
    (function(i){
        $('.class').eq(i).bind('click', function(){
            b[i]();
        });
    }(i));
}

You can also just set the function from the array as the click handler directly.

for(var i = 0; i < b.length; i++){
    $('.class').eq(i).bind('click', b[i]);
}
gen_Eric
  • 223,194
  • 41
  • 299
  • 337