循环中的EventListeners[JavaScript]



我在shapes数组中存储了一些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->2)时,它也总是会给出id->1(最后弹出)。请解释这种行为以及可能的有效方法。

请参阅此处

使用undercore.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的当前值,在循环执行之后,S将是shapes数组中的最后一个pieChart对象。

在回调中,使用this,而不是通过id再次访问元素。

考虑到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将具有上一次迭代的值。

您需要通过回调工厂(如此相关答案所示)或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表)。

您可以使用

e.target.id

而是

S.id

事件中侦听器

相关内容

  • 没有找到相关文章

最新更新