skip to Main Content

I have a webpage which I’m writing an extension for which detects 1 or more table/div sections and I want to insert a button into each to then call a function and pass in dynamic parameters to another function. The problem I’m running into is the dynamic values are always the last in the loop even though the button IDs appear correct in the page source (dynamic). Simplified code extract below:

for (var milestone_id in all_milestones) {
    milestone = all_milestones[milestone_id];
    button = document.createElement("input");
    button.type = "button";
    button.setAttribute("id", "callout_" + milestone_id);
    button.setAttribute("style", "float: right");
    button.value = "Create/Edit Details";
    button.onclick = function() {open_browser(award_id, milestone_id, milestone["date"], env)};
    milestone["class"].appendChild(button);
}

Current behaviour – buttons are successfully injected in with unique IDs (callout_{milestone_id}) but when invoking the function (open_browser) the milestone_id is static // the last value of milestone in the array.

Desired behaviour – as above but ensuring the correct dynamic values are sent through as per the time that the button is injected into the form.

2

Answers


  1. You are using a for...in loop in combination with var for the loop variable. This makes it a global variable and it is overwritten each iteration and not local to the loop. You cannot use that in a closure kind of way like this. Use let or const for the loop variable instead.

    for (let milestone_id in all_milestones) {
      ...
    }
    

    See this MDN-Reference for more information on for...in

    See this MDN-Reference for more information on var

    See this MDN-Reference for more information on const

    See this MDN-Reference for more information on let

    Login or Signup to reply.
  2. Add the different variables (the variables that you used as parameters for the function) as either data-* attributes (like the button.dataset.milestone_id in the example) or as properties (like the button.award_id in the example) on the button. If it is a number or a string you can just use the data-* attribute. If the variable is an object or an element (like a reference to a div or table) you can add it as a property.

    And then in the click event callback function you get the button by e.target and then you can access either standard attributes, data-* attributes or properties.

    const all_milestones = [1, 2, 3];
    
    const open_browser = e => {
      let button = e.target;
      console.log(button.id, button.dataset.milestone_id, button.award_id);
    };
    
    for (var milestone_id in all_milestones) {
      let milestone = all_milestones[milestone_id];
      button = document.createElement("button");
      button.type = "button";
      button.id = "callout_" + milestone_id;
      button.dataset.milestone_id = milestone_id; // data-* attribute
      button.award_id = 'some award id'; // property on button
      button.dataset.milestone_date = 'some date';
      button.dataset.env = 'env';
      button.textContent = "Create/Edit Details";
      button.addEventListener('click', open_browser);
      document.getElementById('container').appendChild(button);
    }
    <div id="container"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search