0

我正在尝试使用 for 循环使用独特的侦听器和处理程序动态创建按钮,但不幸的是我一定做错了,因为只有最后一个按钮有效。更令人惊讶的是,当单击最后一个按钮而不是“Button No.3”时,它会返回“Button No.4”

贝娄是代码,这里是一个 jsfiddle http://jsfiddle.net/y69JC/4/

HTML:

<body>
    <div id="ui">
    some text ...
    </div>
</body>

Javascript:

var uiDiv = document.getElementById('ui');
uiDiv.innerHTML = uiDiv.innerHTML + '<br>';

var results = ["Button one","Button two","Button three","Button four"];

for(var n=0;n<results.length;n++)
{
 uiDiv.innerHTML = uiDiv.innerHTML + '<button id="connect'+n+'">option'+n+':'+results[n]+'</button><br>';
 var tempId = document.getElementById('connect'+n);
 tempId.addEventListener('click', function(){console.log("Button No."+n)}, false);
}

谢谢!

4

3 回答 3

2

当您需要关闭时,这是一个经典案例:更改为:

var uiDiv = document.getElementById('ui');
uiDiv.innerHTML = uiDiv.innerHTML + '<br>';

var results = ["Button one", "Button two", "Button three", "Button four"];

for (var n = 0; n < results.length; n++) {
    // Note: do not use .innerHTML. Create new element programatically
    //       and then use .appendChild() to add it to the parent.
    var button = document.createElement('button');
    button.id = 'connect' + n;
    button.textContent = 'option' + n + ':' + results[n];

    uiDiv.appendChild(button);

    button.addEventListener('click', /* this is an inline function */ (function (n2) {
        // Here we'll use n2, which is equal to the current n value.
        // Value of n2 will not change for this block, because functions
        // in JS do create new scope.

        // Return the actual 'click' handler.
        return function () {
            console.log("Button No." + n2)
        };
    })(n)); // Invoke it immediately with the current n value
}

这样做的原因是for循环不会创建范围,因此"Button No. " + n总是以n等于results数组中元素的数量进行评估。

必须做的是创建一个接受n作为参数的内联函数,并使用当前值立即n调用它。然后,此函数将返回 click 事件的实际处理程序。请参阅答案以获取一个不错且简单的示例。

编辑:您的代码正在使用innerHTML属性在循环中添加新按钮。它被破坏了,因为每次分配 usinguiDiv.innerHTML = ...时,都会删除此 div 中先前存在的所有内容并从头开始重新创建它们。这导致撕掉之前附加的所有事件处理程序。从示意图上看,它看起来像这样:

uiDiv.innerHTML = ""

// First iteration of for-loop
uiDiv.innerHTML === <button1 onclick="...">

// Second iteration of for-loop
// 'onclick' handler from button1 disappeared
uiDiv.innerHTML === <button1> <button2 onclick="...">

// Third iteration of for-loop
// 'onclick' handler from button2 disappeared
uiDiv.innerHTML === <button1> <button2> <button3 onclick="...">
于 2013-08-26T16:08:12.907 回答
0

事件处理程序绑定到n,它results.length在事件触发时具有值。

您必须通过复制来关闭的值,您可以通过调用函数来做到这一点。n这种结构称为闭包

for(var n=0;n<results.length;n++)
{
 uiDiv.innerHTML = uiDiv.innerHTML + '<button     id="connect'+n+'">option'+n+':'+results[n]+'</button><br>';
 var tempId = document.getElementById('connect'+n);
 tempId.addEventListener('click', (function(bound_n) {
   console.log("Button No."+ bound_n);
 })(n)), false); // <-- immediate invocation
}

如果n是一个对象,这将不起作用,因为对象(与标量不同)是通过引用传递的。

避免在所有 for 循环中编写闭包的好方法是不使用 for 循环,而是使用带有回调的 map 函数。在这种情况下,您的回调就是您的闭包,因此您可以免费获得预期的行为:

function array_map(array, func) {
    var target = [], index, clone, len;
    clone = array.concat([]); // avoid issues with delete while iterate
    len = clone.length;
    for (index = 0; index < len; index += 1) {
        target.push(func(clone[index], index));
    }
    return target;
}

array_map(results, function (value, n) {
    uiDiv.innerHTML = uiDiv.innerHTML
      + '<button id="connect'+n+'">option'+n+':'+results[n]+'</button><br>';
    var tempId = document.getElementById('connect'+n);
    tempId.addEventListener('click', function(){console.log("Button No."+n)}, false);    
});
于 2013-08-26T16:10:39.127 回答
0

你可以写

onclick = 'myFunction(this.id):'

在按钮中,然后:

function myFunction(idNum){console.log("Button No."+idNum);}

这里还有一些您可能想要实施的改进:

uiDiv.innerHTML = uiDiv.innerHTML + ....;
uiDiv.innerHTML += ....;

你还想在循环之前声明“uiDivContent”,然后写:

uiDiv.innerHTML = uiDivContent;
于 2013-08-26T16:12:10.797 回答