1

I have a situation whereby, i need to create buttons dynamically and i need to attached an onclick event to each of them. All of the above is done fine. However, when one of the button is clicked, the function that is called i.e (animate(poly,map)) uses the last known value of poly and map as parameters. I stuck with this problem since this morning. Please help. Thanks

for(var k=0;k<arr_altern.length;k++){
         my_div=create_div_interchange(arr[i],1,78,visited_bus,interchange_arr,arr_altern[k],null, my_interchange_array);

        $('#results').append(my_div);
        var x='animate';
        var v='#animater';
        v+=div_id;
        x+=div_id;
        map=create_map(div_id);
        var poly=retrieve_results_edges(bus_stops_visited,map);

        var strVar="";
        strVar += "<span class=\"animate\">";
        strVar += "<input type=\"button\"  id="+x+" name=\"animate\"  value=\"Animate\" \/>";
        strVar += "<\/span>";


        $(v).append(strVar);



        document.getElementById(x).onclick=function test(){

                animate(poly,map);

     }

     set_map(map);


    set_polyline_color(my_traversed_edge,map);

        }

UPDATE SOLUTION:

i've replaced  

    document.getElementById(x).onclick=function test(){

                animate(poly,map);

     }

BY

$('#'+x).bind('click',{poly:poly,map:map}, function(event) {
             animate(event.data.poly,event.data.map)
      });
j.b
  • 151
  • 1
  • 7
  • 17
  • 1
    The problem is that you keep setting the value of the *same* variable (a *global property* in the case of `map`) which is then accessed later. There is only *one* variable called `map` and *one* variable called `poly` in the code. Please search StackOverflow for **"javascript last value loop"** which will lead to examples of what is going wrong, and how to correct it. –  Feb 24 '12 at 23:50
  • yes, of course, you're changing the values of poly and map throughout the loop and the last values stay in. You need to store those of each of the buttons separately – scibuff Feb 24 '12 at 23:52
  • 1
    http://stackoverflow.com/questions/6425062/passing-functions-to-settimeout-in-a-loop-always-the-last-value , http://stackoverflow.com/questions/2520587/variable-in-javascript-callback-functions-always-gets-last-value-in-loop , http://stackoverflow.com/questions/6599157/why-always-the-last-reference-to-the-object-is-used-in-loop , etc. Pick one. –  Feb 24 '12 at 23:52
  • 2
    Okay, found a really good "simple" case of this: http://stackoverflow.com/questions/5555464/javascript-closure-of-loop –  Feb 24 '12 at 23:57
  • 1
    You have a syntax error `$(#results).append(my_div);`, missing `"` – elclanrs Feb 25 '12 at 00:03

3 Answers3

3

You are declaring the map variable without the var keyword so it's being created in the global scope, so there is only one map variable that gets it's value over-written each loop.

for(var k=0;k<arr_altern.length;k++){
    (function (k) {
        my_div=create_div_interchange(arr[i],1,78,visited_bus,interchange_arr,arr_altern[k],null, my_interchange_array);

        $('#results').append(my_div);
        var x      = 'animate' + div_id,
            v      = '#animater' + div_id,
            map    = create_map(div_id),
            poly   = retrieve_results_edges(bus_stops_visited, map),
            strVar = '<span class="animate"><input type="button"  id="' + x + '" name="animate"  value="Animate" /><\/span>';

        $(v).append(strVar);

        document.getElementById(x).onclick=function test(){

            animate(poly,map);

        }

        set_map(map);


        set_polyline_color(my_traversed_edge,map);
    })(k);
}

Running your code inside of an IIFE (Immediately-Invoked Function Expression) will create a new scope for the code within it. This allows you to declare variables at the time of running and they will hold their value into the future (for instance they will be available to event handlers that fire in the future). Notice I used the var keyword when declaring all the variables so they are created in the current scope.

Update

You could also use $.each() to scope your code:

$.each(arr_altern, function (k, val) {
        my_div=create_div_interchange(arr[i],1,78,visited_bus,interchange_arr,arr_altern[k],null, my_interchange_array);

        $('#results').append(my_div);
        var x      = 'animate' + div_id,
            v      = '#animater' + div_id,
            map    = create_map(div_id),
            poly   = retrieve_results_edges(bus_stops_visited, map),
            strVar = '<span class="animate"><input type="button"  id="' + x + '" name="animate"  value="Animate" /><\/span>';

        $(v).append(strVar);

        document.getElementById(x).onclick=function test(){

            animate(poly,map);

        }

        set_map(map);


        set_polyline_color(my_traversed_edge,map);
});
Community
  • 1
  • 1
Jasper
  • 75,717
  • 14
  • 151
  • 146
0

Try to use self-invocation

(function (x, poly, map) {

     document.getElementById(x).onclick=function test(){

                animate(poly,map);

     }


})(x, poly, map);
Jasper
  • 75,717
  • 14
  • 151
  • 146
Slawek
  • 583
  • 3
  • 9
  • Yes, but why? How does it change things? –  Feb 24 '12 at 23:58
  • @pst By passing the vars as parameters to a function, and calling it immediately, you bind (copy) the _current_ values of the variables to that function's scope. JavaScript only has function scope, not block scope. – calebds Feb 25 '12 at 00:04
  • @paislee You don't pass variables ;-) [nit, nit, just trying to flush out this answer some] –  Feb 25 '12 at 00:11
  • I got the solution. I've used the JQuery bind method. Thanks for all suggestions! – j.b Feb 25 '12 at 00:19
  • @pst I like precisions. What, pray tell, does one pass? – calebds Feb 25 '12 at 02:25
  • @paislee The values which result from the evaluation of the expressions ("current values of the variables") - basically, take off the bit before the first "," :P –  Feb 25 '12 at 02:40
0

You can use jQuery proxy

var buttons = $("a");
for (var i = 0; i < buttons.length; i++){
  $(buttons[i]).unbind("click").click(
    $.proxy(
      function(){
        alert(this);
        return false;
      },
      i
    )
  );
}

or capture your arguments by creating new function

var buttons = $("a");
for (var i = 0; i < buttons.length; i++){
  $(buttons[i]).unbind("click").click(
    function(arg){
      return function(){
        alert(arg);
        return false;
      };
    }(i)
  );
}

Just run one of this examples in firebug console to see the effect.

Razoomnick
  • 304
  • 4
  • 5