2013-08-26 85 views
0

我试图通过使用for循环动态地创建具有唯一侦听器和处理程序的按钮,但不幸的是,由于只有最后一个按钮的作用,我必须做错事情。 更令人惊讶的是,点击最后一个按钮,而不是“按钮3号”,当它返回“按钮四号”我如何在javascript中动态创建事件和事件处理程序

贝娄的代码,这里是一个的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); 
} 

谢谢!

回答

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事件的实际处理程序。请参阅this回答一个很好的简单示例。

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

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="..."> 
+0

是的,你可以做到这一点,但我觉得这可能不是在IE <9工作示例,请参阅[这个小提琴](http://jsfiddle.net/7pHkw/)。基本上,它涉及到使用[bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ Global_Objects /功能/绑定?redirectlocale = EN-US&redirectslug = JavaScript的%2FReference%2FGlobal_Objects%2FFunction%2Fbind)。 – kamituel

+0

我在jsfiddle上试过你的代码,它不工作...我修改了它,使其通过将EventListener更改为:tempId.addEventListener('click',(function(n2){return function(){ console.log(“Button No。”+ n2)}})(n),false);但它像我的代码一样工作......任何想法? [jsfiddle](http://jsfiddle.net/y69JC/5/) – Manolis

+0

@Manolis - 你使用什么浏览器?顺便说一句,我的答案仍然有效,它回答你的原始问题 - 我不明白你为什么删除了“接受”的标志。 – kamituel

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);  
}); 
+0

我试过你的代码,但我得到一个控制台错误:'未捕获的SyntaxError:意外的令牌)' – Manolis

+0

我没有得到那个错误,但看看行和字符的位置,这可能会告诉你错误在哪里。 – Halcyon

0

你可以写

onclick = 'myFunction(this.id):' 
在按钮

再后来:

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

也在这里是您可能想要实施的一些改进:

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

你也想在循环之前申报“uiDivContent”然后后来写:

uiDiv.innerHTML = uiDivContent; 
相关问题