0

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
  • How do you insert the buttons? Commented Sep 7, 2023 at 4:45
  • Sorry forgot to include this -> milestone["class"].appendChild(button); The milestone["class"] is just the class / div that the button resides Commented Sep 7, 2023 at 4:53

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for suggestion, just tried seems to be same behaviour. I was looking for the deepcopy python equivalent in javascript as I know the problem just not the method to prevent it :)
try an arrow/lambda function maybe instead of an anonymous one: ()=>{} and try addEventListener('click', ()=>{...})
Also on a sidenote. Your code style is a bit inconsistent. You are using setAttribute only for id and style but not for type, value and onclick. And you are using snake_case, in js better stick to camelCasing
ah man maybe I was wrong. You are using for...in combined with var which makes it a global variable. Use let instead! Sorry for the trouble! I updated my answer...
another suggestion: never use var again, always use const or let instead. var was never a good idea to begin with in JS
|
1

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.