1

I have an object that has a list of JSONs in specific format as an attribute and from this list I want to create a set of buttons, which should execute the same function, but with arguments given by the JSONs. Below I wrote this as a simple example of how I wanted to do this. Each button should log the number that it is labelled with. So "one" prints 1, "two" prints 2 etc., but instead each button prints 4.

Now the problem is (a least to my understanding of javascript) late-binding. Namely that all functions will be created as late as possible, which is why all buttons will print the last value of "value". How do I avoid this? My attempt with "var self = this" also did not work.

function My_object(div) {
  this.list = [{
    label: "one",
    value: 1
  }, {
    label: "two",
    value: 2
  }, {
    label: "three",
    value: 3
  }, {
    label: "four",
    value: 4
  }];
  this.div = div;
}

My_object.prototype.createButtonsInDiv = function() {
  var self = this,
    i = 0,
    label = '',
    value = 0;
  for (i = 0; i < this.list.length; i++) {
    label = this.list[i].label;
    value = this.list[i].value;
    this.div.appendChild(newButton(label, function() {
      console.log(value); // will always print '4'
      console.log(self.list[i].value); // will always get an error
    }));
  }
};

function newButton(label, func) {
  var button = document.createElement('button');
  var buttonText = document.createTextNode(label);
  button.appendChild(buttonText);
  button.addEventListener("click", func);
  return button;
}

var my_div = document.getElementById('my_div');
var o = new My_object(my_div);
o.createButtonsInDiv();

The html in this example consists of a single div with id="my_div".

Truning
  • 107
  • 1
  • 10
  • Classical way to solve this : wrap the relevant parts in a new closure, with an intermediate function. (see marked duplicate for examples) – Pac0 Dec 17 '18 at 10:30
  • "*Namely that all functions will be created as late as possible*" - no, that's not the reason. The function get created immediately. The problem is that they all close over the same variables, which can only hold one value at a time. – Bergi Dec 17 '18 at 10:31
  • Your current code seems to be working fine though: http://jsfiddle.net/AndrewL64/qgw42z8y/1/ The `divs` are rendered with the values 1,2,3,4 respectively as you wanted. – AndrewL64 Dec 17 '18 at 10:34
  • for avoiding that closure issue, you can just start your label and value variables inside for loop with as const. Then, your code will work. I'd recommend to check differences between var, let, const based on closures – Doğancan Arabacı Dec 17 '18 at 10:37
  • In the function `createButtonsInDiv` within the `for` loop, change all calls to `list` from `this.list..` to `self.list..`, that might solve it but is just a wild guess, your code looks fine. – Bargros Dec 17 '18 at 10:38
  • @Pac0 Thanks a lot. I could solve it now. – Truning Dec 17 '18 at 16:10
  • @Bergi Thank you for the clarification – Truning Dec 17 '18 at 16:14
  • @AndrewL Yes, the buttons get labelled correctly, but they do not log the correct value, which was my problem. – Truning Dec 17 '18 at 16:14
  • @DoğancanArabacı I should have a look a closures then, thank you! – Truning Dec 17 '18 at 16:14
  • @Bargros No, unfortunately this does not solve the problem. As Pac0 pointed out, it had to to with closures. – Truning Dec 17 '18 at 16:15

0 Answers0