动态创建的 js 脚本 onClick 函数变量不绑定当前值

dynamically created js script onClick function variables not binding at current value

newButton.onclick = function() {
  launchForm(
    this, 
    incoming[(10 * (incoming.length / fields)) + z],
    incoming[(48 * (incoming.length / fields)) + z]);
};

因此,当我动态生成其中的 4 个按钮时,这四个按钮中的每一个都将第一个按钮的信息传递到函数 launchForm 中。我怀疑这是因为当变量 z 完成它的 for 语句时它返回到 0。如果是这样的话,而不是 + z 是 + 1 按钮 1 和 + z 是 + 2 按钮 2 它变成 + z 是 + 0对于所有按钮。

我该如何解决这个问题?谢谢

编辑:已添加以响应更多信息请求:

    var newButton = document.createElement('button');
    newButton.className = 'pure-button pure-button-primary mikeButton';
    newButton.innerHTML='Launch Form';
    newDiv4.appendChild(newButton);

所以我对这一切都很陌生,所以我可能用错了术语。我理解页面加载后创建的项目被认为是动态的。 --- 虽然我是新手,但我非常有信心问题出在我最初提出的问题上。通过设置警报和测试,我可以看到第一个按钮完全正常工作,并且所有适当的参数都完美传递。只是按钮 2、3 和 4 也会启动按钮 1 启动的内容。 Incoming.length不变,字段不变。与创建 newButton 的第一个实例到第二个实例时唯一不同的是 z。 --- 当我在创建第二个、第三个和第四个按钮后立即设置警报时,它们会准确显示应该传递的参数。但是在创建它们的函数完成后(基于使用 z 作为变量的 for 语句),它们转换回就好像 z 是 0 并使用按钮 1 的参数(因为在按钮 1 的参数中 z 等于 0)。我只是将 + 1、+2、+3 硬编码到代码中而不是 z,但是创建的按钮数量取决于查询。有时 1 个按钮,有时 10 个。所以这真的不是一个选择。

希望对您有所帮助。

ES5 不使用块上下文,而是在函数上下文上运行。这意味着在 for 循环中您必须立即使用索引变量,否则如果您(例如)在回调函数中引用它,您将获得回调执行期间该变量的值。

有多种方法可以解决这个问题,可能最简单的方法是在代码中演示:

"use strict";

// first off, using [].forEach.call has some cons so we should have 
// a way to convert a NodeList to an Array
var toArray = function(input) {
  var output = [];
  // check if we have an array-like object
  if (input && typeof input.length === 'undefined') { return; }
  for (var i = 0; i < input.length; i++) {
    output.push(input[i]);
  }
  return output;
}

// we get lists that contain links to parse
var linkLists = document.getElementsByClassName('link-list');
// and one div to store buttons
var buttonDiv = document.getElementById('buttons');

// we convert the NodeLists to arrays to get the forEach function
toArray(linkLists).forEach(function(list) {

  // now we iterate over the list children
  toArray(list.children).forEach(function(link, index) {

    var clickHandler = function(e) {
      e.preventDefault(); // prevent navigating anywhere
      alert("Hi! I'm a link named '" + link.textContent +
            "' and my forEach index is: " + index +
            " (" + typeof index + ")");
    }

    link.onclick = clickHandler;

    // now let's use the link list as a mock data source and generate buttons
    var newButton = document.createElement('button');
    newButton.textContent = link.textContent;
    newButton.onclick = clickHandler;
    buttonDiv.appendChild(newButton);
    
  });
});
<ul class="link-list">
  <li><a href="#">link 1</a></li>
  <li><a href="#">link 2</a></li>
  <li><a href="#">link 3</a></li>
</ul>
<div id="buttons"></div>

有关 why [].forEach.call is discouraged 的更多详细信息。

现在来说明常见的陷阱是什么:

"use strict";

// we get lists that contain links to parse
var linkLists = document.getElementsByClassName('link-list');
// and one div to store buttons
var buttonDiv = document.getElementById('buttons');

// let's switch the forEach calls to for loops
// variables added for naming sanity
for (var x = 0; x < linkLists.length; x++) {
  var children = linkLists[x].children;
  
  for (var y = 0; y < children.length; y++) {
    var link = children[y],
        index = y;
    
    // everything is fine up to this point
    // now as there's no block context that would keep the variable
    // values for the handler we'll get the last value it had
    var clickHandler = function(e) {
      e.preventDefault(); // prevent navigating anywhere
      alert("Hi! I'm (possibly) a link named '" + link.textContent +
            "' and my for index is: " + index +
            " (" + typeof index + ") \n\n :(");
    }

    link.onclick = clickHandler;
  }
  
}

// now, what few beginners remember is that when there's no block
// context then all of the variables used above are available outside

// let's reuse the buttons div to get all variable values here
buttons.innerHTML = "Variable values after parsing for loops:" +
                    "<br/>x = " + x +
                    "<br/>y = " + y +
                    "<br/>link.textContent = " + link.textContent + 
                    "<br/>index = " + index;

// so now when we click the link the callback function gets
// the variables from above and not from it's block context
// as it would in other programming languages
<ul class="link-list">
  <li><a href="#">link 1</a></li>
  <li><a href="#">link 2</a></li>
  <li><a href="#">link 3</a></li>
</ul>
<div id="buttons"></div>

单击时所有处理程序都引用 z 的当前值。要在定义中使用 z 的值,我建议

  1. 通过调用 Function 作为构造函数来创建 onclick 处理程序,并计算正文字符串中 z 的当前值:

    newButton.onclick = new Function("launchForm(this, incoming[(10 * (incoming.length / fields)) + "+ z + "], incoming[(48 * (incoming.length / fields)) + " + z  + "]);");
    
  2. 或者在创建 onclick 处理程序时将 z 的值保存在按钮对象上:

    newButton["data-z"] = z;
    newButton.onclick = function() {....};
    

但在函数体中将对 "z" 的引用替换为 "this['data-z']"。