Saturday 6 July 2019

javascript - About a loop that creates dynamic buttons, but cannot give proper values











I am having a small issue, and it would be very nice if some of you could realize about what kind of logic is missing here, since I cannot seem to find it:



I have an array with the results of some previous operation. Let's say that the array is:



var results = [0, 1];


And then I have a bunch of code where I create some buttons, and inside a for loop I assign a different function to those buttons, depending on the position of the array. The problem is that for some reason, all the buttons created (two in this case) come out with the function assigned to the last value of the array (in this case, both would come out as one, instead of the first with 0 and the second with 1)




This is the code:



for (var i = 0; i < results.length; i++) {
var br2 = b.document.createElement("br");
var reslabel = b.document.createTextNode(Nom[results[i]].toString());
var card = document.createElement("input");
card.type = "button";
id = results[i]; // this is the problematic value.
card.onclick = newcard; // this function will use the above value.
card.value = "Show card";

divcontainer.appendChild(br2);
divcontainer.appendChild(reslabel);
divcontainer.appendChild(card);
}


As it is, this code produces as many buttons as elements in the array, each with its proper label (it retrieves labels from another array). Everything is totally fine. Then, I click the button. All the buttons should run the newcard function. That function needs the id variable, so in this case it should be:




  • First button: runs newcard using variable id with value 0


  • Second button: runs newcard using variable id with value 1



But both buttons run using id as 1... why is that?



It might be very simple, or maybe is just that in my timezone is pretty late already :-) Anyways, I would appreciate any comment. I am learning a lot around here...



Thanks!



Edit to add the definition of newcard:




function newcard() {
id = id;
var toerase = window.document.getElementById("oldcard");
toerase.innerHTML = "";
generate();
}


the function generate will generate some content using id. Nothing wrong with it, it generates the content fine, is just that id is always set to the last item in the array.



Answer



Your id is a global variable, and when the loop ends it is set to the last value on the array. When the event handler code runs and asks for the value of id, it will get that last value.



You need to create a closure to capture the current results[i] and pass it along (this is a very common pitfal, see Javascript infamous Loop problem?). Since newcard is very simple, and id is actually used in generate, you could modify generate to take the id as a parameter. Then you won't need newcard anymore, you can do this instead:



card.onclick = (function(id) { 
return function() {
window.document.getElementById("oldcard").innerHTML = "";
generate(id);
};

}(results[i]));


What this does is define and immediately invoke a function that is passed the current results[i]. It returns another function, which will be your actual onclick handler. That function has access to the id parameter of the outer function (that's called a closure). On each iteration of the loop, a new closure will be created, trapping each separate id for its own use.


No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...