addEventListener使用for循环和传递值

时间:2013-10-25 09:32:56

标签: javascript for-loop addeventlistener

我正在尝试使用for循环向多个对象添加事件侦听器,但最终会使用针对同一对象的所有侦听器 - >最后一个。

如果我通过为每个实例定义boxa和boxb来手动添加侦听器,它就可以工作。我想这就是addEvent for-loop,它不像我希望的那样工作。也许我完全使用了错误的方法。

使用4个class =“container”的示例 容器4上的触发按照预期的方式工作。 触发容器1,2,3触发容器4上的事件,但仅在触发器已被激活时才会触发。

// 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);
}
<div class="container">
  <div class="one" id="box1">
    <p class="triggerClass">some text</p>
  </div>
  <div class="two" id="box2">
    <p class="triggerClass">some text</p>
  </div>
</div>

<div class="container">
  <div class="one" id="box3">
    <p class="triggerClass">some text</p>
  </div>
  <div class="two" id="box4">
    <p class="triggerClass">some text</p>
  </div>
</div>

5 个答案:

答案 0 :(得分:109)

闭包! :d

此固定代码按预期工作:

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
}

为什么要修复它?

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。它的解释如下:

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

由于variable hoistingvar声明会移到范围的顶部。由于JavaScript没有块范围(forifwhile等),因此它们会移到函数的顶部。 更新:从ES6开始,您可以使用let来获取块范围变量。

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

使用闭包(关闭boxaboxb等的值)将值绑定到单击处理程序的范围。


此类JSLintJSHint等代码分析工具将能够捕获此类可疑代码。如果您正在编写大量代码,那么花些时间学习如何使用这些工具是值得的。有些IDE内置它们。

答案 1 :(得分:11)

您将范围/结束问题视为function(){makeItHappen(boxa,boxb);} boxa boxb 引用,然后始终是最后一个元素。

解决问题:

function makeItHappenDelegate(a, b) {
  return function(){
      makeItHappen(a, b)
  }
}

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

答案 2 :(得分:6)

你可以使用Function Binding。你不需要使用闭包。见下文:

<强>之前

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

<强>后

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

答案 3 :(得分:1)

这是因为关闭。

检查出来:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

示例代码和您的代码基本相同,对于那些不知道“封闭”的人来说,这是一个常见的错误。

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

由于您的处理程序“记住”i,因此i执行后变量addEvents()不会消失。

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

答案 4 :(得分:0)

前一段时间我也遇到过这个问题。 我通过在循环外部使用“添加”功能来解决它​​,分配事件,并且它工作得很好。

您的脚本应该是这样的。

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";
}