关于javascript:addEventListener用于循环和传递值

addEventListener using for loop and passing values

本问题已经有最佳答案,请猛点这里访问。

我试图使用for循环向多个对象添加事件侦听器,但最终所有侦听器都指向同一对象——>最后一个对象。

如果我通过为每个实例定义boxa和boxb来手动添加监听器,那么它就工作了。我想是循环的addEvent没有按我希望的方式工作。也许我完全使用了错误的方法。

使用class="container"的4个示例容器4上的触发器按预期方式工作。容器1、2、3上的触发器容器4上的触发器事件,但仅当已激活触发器时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Function to run on click:
function makeItHappen(elem, elem2) {
  var el = document.getElementById(elem);
  el.style.backgroundColor ="red";
  var el2 = document.getElementById(elem2);
  el2.style.backgroundColor ="blue";
}

// Autoloading function to add the listeners:
var elem = document.getElementsByClassName("triggerClass");

for (var i = 0; i < elem.length; i += 2) {
  var k = i + 1;
  var boxa = elem[i].parentNode.id;
  var boxb = elem[k].parentNode.id;

  elem[i].addEventListener("click", function() {
    makeItHappen(boxa, boxb);
  }, false);
  elem[k].addEventListener("click", function() {
    makeItHappen(boxb, boxa);
  }, false);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    <p class="triggerClass">some text
</p>
 
 
    <p class="triggerClass">some text
</p>
 



 
    <p class="triggerClass">some text
</p>
 
 
    <p class="triggerClass">some text
</p>


关闭!D

此固定代码按您的预期工作:

1
2
3
4
5
6
7
8
9
10
for (var i = 0; i < elem.length; i += 2) {
    (function () {
        var k = i + 1;
        var boxa = elem[i].parentNode.id;
        var boxb = elem[k].parentNode.id;

        elem[i].addEventListener("click", function() { makeItHappen(boxa,boxb); }, false);
        elem[k].addEventListener("click", function() { makeItHappen(boxb,boxa); }, false);
    }()); // immediate invocation
}

为什么要修复它?

1
2
3
4
5
6
7
8
for(var i=0; i < elem.length; i+=2){
    var k = i + 1;
    var boxa = elem[i].parentNode.id;
    var boxb = elem[k].parentNode.id;

    elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false);
    elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false);
}

实际上是非严格的javascript。解释如下:

1
2
3
4
5
6
7
8
9
var i, k, boxa, boxb;
for(i=0; i < elem.length; i+=2){
    k = i + 1;
    boxa = elem[i].parentNode.id;
    boxb = elem[k].parentNode.id;

    elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false);
    elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false);
}

由于变量提升,var声明被移动到范围的顶部。因为javascript没有块范围(forifwhile等),所以它们被移到了函数的顶部。更新:从ES6开始,可以使用let获取块范围变量。

当代码运行时,会发生以下情况:在for循环中,添加单击回调,然后分配boxa,但其值在下一次迭代中会被覆盖。当click事件触发时,回调运行,并且boxa的值始终是列表中的最后一个元素。

使用一个闭包(关闭boxaboxb等的值)将值绑定到单击处理程序的作用域。

JSlint或JShint等代码分析工具将能够捕获这样的可疑代码。如果你写了很多代码,花点时间学习如何使用这些工具是值得的。有些IDE内置了它们。


您将面临范围/关闭问题,因为function(){makeItHappen(boxa,boxb);}boxa和boxb引用,然后总是最后一个元素。

要解决此问题:

1
2
3
4
5
6
7
8
function makeItHappenDelegate(a, b) {
  return function(){
      makeItHappen(a, b)
  }
}

// ...
 elem[i].addEventListener("click", makeItHappenDelegate(boxa,boxb), false);


可以使用函数绑定。不需要使用闭包。请参见下面的内容:

之前:

1
2
3
4
5
6
7
8
9
10
11
12
function addEvents(){
   var elem = document.getElementsByClassName("triggerClass");

   for(var i=0; i < elem.length; i+=2){
      var k = i + 1;
      var boxa = elem[i].parentNode.id;
      var boxb = elem[k].parentNode.id;

      elem[i].addEventListener("click", function(){makeItHappen(boxa,boxb);}, false);
      elem[k].addEventListener("click", function(){makeItHappen(boxb,boxa);}, false);
   }
}

后:

1
2
3
4
5
6
7
8
9
10
11
function addEvents(){
   var elem = document.getElementsByClassName("triggerClass");

   for(var i=0; i < elem.length; i+=2){
        var k = i + 1;
      var boxa = elem[i].parentNode.id;
      var boxb = elem[k].parentNode.id;
      elem[i].addEventListener("click", makeItHappen.bind(this, boxa, boxb), false);
      elem[k].addEventListener("click", makeItHappen.bind(this, boxa, boxb), false);
   }
}


因为关闭了。

查看以下内容:https://developer.mozilla.org/en-us/docs/web/javascript/closures在循环中创建闭包常见错误

示例代码和您的代码本质上是相同的,对于那些不知道"闭包"的人来说,这是一个常见的错误。

简单来说,当您在addEvents()中创建一个处理函数时,它不仅从addEvents()的环境访问变量i,而且还"记住"i

因为处理程序"记住"i,所以执行addEvents()后,变量i不会消失。

因此,当调用处理程序时,它将使用i,但变量i现在在for循环3之后。


我之前也遇到过这个问题。我通过在循环外部使用一个"添加"函数来解决这个问题,以分配事件,并且它工作得很好。

你的剧本应该是这样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function addEvents(){
  var elem = document.getElementsByClassName("triggerClass");
  for(var i=0; i < elem.length; i+=2){
    var k = i + 1;
    var boxa = elem[i].parentNode.id;
    var boxb = elem[k].parentNode.id;

//- edit ---------------------------|
    adds(boxa, boxb);
  }
}
//- adds function ----|
function adds(obj1, obj2){
  obj1.addEventListener("click", function(){makeItHappen(obj1, obj2);}, false);
  obj2.addEventListener("click", function(){makeItHappen(obj1, obj2);}, false);
}
//- end edit -----------------------|

function makeItHappen(elem, elem2){
  var el = document.getElementById(elem);
  el.style.transform ="flip it";
  var el2 = document.getElementById(elem2);
  el2.style.transform ="flip it";
}