我有几个存储在形状数组中的pieChart对象...我想为每个对象添加事件侦听器。我尝试这样做:
var tempS = shapes.slice();
for(var i=0; i<shapes.length; i++)
{
var S = tempS.pop();
if(S.name == 'pieChart')
{
document.getElementById(S.id).addEventListener('mousedown', function(e){
alert(S.id);
}, false);
}
}
这里的问题是,即使我点击了pieChart2(id-&gt; 2),它也总会给出id-&gt; 1(因为它弹出最后一个)。请解释这种行为以及可能有效的方法。
答案 0 :(得分:1)
请参阅here
使用underscore.js:
_.each(shapes, function(s) {
if(s.name === 'pieChart') {
document.getElementById(s.id).addEventListener('mousedown',function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
alert(target.id);
});
}
});
要解释代码的行为,您必须记住javascript使用函数作用域而不是块作用域。因此,由于您在eventListener回调函数之外定义了S,因此回调中S的引用将是S的当前值,在循环执行之后将是形状数组中的最后一个pieChart对象。
答案 1 :(得分:1)
在内部回调中,使用this
而不是再次按id
访问元素。
答案 2 :(得分:0)
您可以使用
e.target.id
代替
S.id
事件监听器中的
答案 3 :(得分:0)
考虑到var
声明具有函数作用域(如果在全局作用域中声明,则为全局),您的代码等同于:
var tempS = shapes.slice(),
i, S;
for (i = 0; i < shapes.length; i++) {
S = tempS.pop();
if (S.name == 'pieChart') {
document.getElementById(S.id).addEventListener('mousedown', function (e) {
alert(S.id);
}, false);
}
}
现在,很明显S
的范围在for
块之外,并且由于事件处理程序将在循环结束后触发,S
将具有最后一次迭代。
您需要通过回调工厂(如此related answer)或IIFE创建新范围,以捕获迭代的当前S
值。
var tempS = shapes.slice();
for (var i = 0; i < shapes.length; i++) { (function() {//introduces a new scope
var S = tempS.pop(); //now S is scoped inside this iteration's IIFE
if (S.name == 'pieChart') {
document.getElementById(S.id).addEventListener('mousedown', function (e) {
alert(S.id); //will reference the IIFE scope's S
}, false);
}
}()); }
您也可以通过多种替代方式执行此操作,例如,使用Array#forEach:
var tempS = shapes.slice();
shapes.forEach(function(S) { //S is now scoped in the forEach callback scope
if (S.name == 'pieChart') {
document.getElementById(S.id).addEventListener('mousedown', function (e) {
alert(S.id);
}, false);
}
});
请注意,这会改变迭代顺序,但我相信这个用例无关紧要。
在ES6中,可以使用let
创建块范围的变量:
for (var i = 0; i < shapes.length; i++) {
let S = tempS.pop(); //scoped inside the `for` block
if (S.name == 'pieChart') {
document.getElementById(S.id).addEventListener('mousedown', function (e) {
alert(S.id);
}, false);
}
}
我将此方法留给未来的读者,因为let
尚未准备好进行制作。最近的浏览器确实有实验性实现(参见ES6 compat table)。