我想使用事件监听器来防止使用onclick函数在div内的div上冒泡。这是有效的,传递参数我的意图:
<div onclick="doMouseClick(0, 'Dog', 'Cat');" id="button_id_0"></div>
<div onclick="doMouseClick(1, 'Dog', 'Cat');" id="button_id_1"></div>
<div onclick="doMouseClick(2, 'Dog', 'Cat');" id="button_id_2"></div>
<script>
function doMouseClick(peram1, peram2, peram3){
alert("doMouseClick() called AND peram1 = "+peram1+" AND peram2 = "+peram2+" AND peram3 = "+peram3);
}
</script>
但是,我尝试在循环中创建多个事件侦听器:
<div id="button_id_0"></div>
<div id="button_id_1"></div>
<div id="button_id_2"></div>
<script>
function doMouseClick(peram1, peram2, peram3){
alert("doMouseClick() called AND peram1 = "+peram1+" AND peram2 = "+peram2+" AND peram3 = "+peram3);
}
var names = ['button_id_0', 'button_id_1', 'button_id_2'];
for (var i=0; i<names.length; i++){
document.getElementById(names[i]).addEventListener("click", function(){
doMouseClick(i, "Dog", "Cat");
},false);
}
</script>
它正确地将click函数分配给每个div,但每个div的第一个参数peram1
为3
。我期待3个不同的事件处理程序为i
传递peram1
的不同值。
为什么会这样?事件处理程序不是全部分开吗?
答案 0 :(得分:19)
问题是闭包,因为JS没有块作用域(只有函数作用域)i
不是您的想法,因为事件函数创建了另一个作用域,因此当您使用i
时它已经是来自for
循环的最新值。您需要保留i
的价值。
使用IIFE:
for (var i=0; i<names.length; i++) {
(function(i) {
// use i here
}(i));
}
使用forEach
:
names.forEach(function( v,i ) {
// i can be used anywhere in this scope
});
答案 1 :(得分:3)
正如已经指出的那样,问题在于闭包和变量范围。确保传递正确值的一种方法是编写另一个返回所需函数的函数,将变量保存在正确的范围内。 jsfiddle
var names = ['button_id_0', 'button_id_1', 'button_id_2'];
function getClickFunction(a, b, c) {
return function () {
doMouseClick(a, b, c)
}
}
for (var i = 0; i < names.length; i++) {
document.getElementById(names[i]).addEventListener("click", getClickFunction(i, "Dog", "Cat"), false);
}
为了说明一种方法,你可以用一个对象来做到这一点:
var names = ['button_id_0', 'button_id_1', 'button_id_2'];
function Button(id, number) {
var self = this;
this.number = number;
this.element = document.getElementById(id);
this.click = function() {
alert('My number is ' + self.number);
}
this.element.addEventListener('click', this.click, false);
}
for (var i = 0; i < names.length; i++) {
new Button(names[i], i);
}
或略有不同:
function Button(id, number) {
var element = document.getElementById(id);
function click() {
alert('My number is ' + number);
}
element.addEventListener('click', click, false);
}
for (var i = 0; i < names.length; i++) {
new Button(names[i], i);
}
答案 2 :(得分:1)
javascript中的一切都是全球性的。它调用变量i
,在循环后设置为3 ...如果在循环后将i
设置为1000,那么您会看到每个方法调用为i
生成1000
如果要维护状态,则应使用对象。让对象具有您为click方法指定的回调方法。
你提到为事件冒泡做这个...为了停止事件冒泡,你真的不需要它,因为它内置于语言中。如果您确实想要阻止事件冒泡,那么您应该使用传递给回调的stopPropagation()
对象的event
方法。
function doStuff(event) {
//Do things
//stop bubbling
event.stopPropagation();
}
答案 3 :(得分:1)
这是因为关闭。
示例代码和您的代码基本相同,对于那些不知道“封闭”的人来说,这是一个常见的错误。
简单来说,当你创建一个处理函数时,它不只是从外部环境访问变量i
,而且还“记住”i
。
因此,当调用处理程序时,它将使用i
,但变量i
现在是for循环之后的2。
答案 4 :(得分:1)
我已经为这个问题苦苦挣扎了几个小时,现在我已经设法解决了。这是我的解决方案,使用function constructor:
function doMouseClickConstructor(peram1, peram2, peram3){
return new Function('alert("doMouseClick() called AND peram1 = ' + peram1 + ' AND peram2 = ' + peram2 + ' AND peram3 = ' + peram3 + ');');
}
for (var i=0; i<names.length; i++){
document.getElementById(names[i]).addEventListener("click", doMouseClickConstructor(i,"dog","cat"));
};
注意:我尚未实际测试此代码。但是,我已经测试了codepen的所有重要功能,因此,如果上面的代码不起作用,我可能只是犯了一些拼写错误。这个概念应该仍然有效。
祝您编程愉快!