addEventListener 使用 for 循环和传递值

IT技术 javascript for-loop addeventlistener
2021-01-31 05:25:56

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

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

在容器 4 上使用 class="container" Trigger 中的 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个回答

关闭!:D

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

// 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) {
    (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
}
<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>


为什么要解决这个问题?

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

由于变量提升var声明被移到了作用域的顶部。由于JavaScript没有块作用域(forifwhile等),他们无法移动到函数的顶部。更新:从 ES6 开始,您可以使用let获取块作用域变量。

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

使用封闭(封闭的值boxaboxb等),你的值绑定到单击处理的范围。


代码分析工具等的JSLintJSHint将能够赶上可疑这样的代码。如果您要编写大量代码,那么花时间学习如何使用这些工具是值得的。一些 IDE 内置了它们。

@musefan 他们是问题所在,因为变量提升。请看我的解释。
2021-03-12 05:25:56
实际上是无效的 JavaScript。: 如果无效,则不会解析或执行。使用我向您展示的闭包,您确实得到了您期望的行为:正确,但重要的一点是您正在循环内执行一个函数,这会创建一个新的作用域。
2021-03-22 05:25:56
应该}(i, k))})(i, k)
2021-03-24 05:25:56
@Felix土豆土豆,将措辞改为非严格@Wayne 要么有效,但这}())是我的偏好。
2021-03-28 05:25:56
@Wayne:无论哪种方式都可以。我个人认为第一个更具可读性。
2021-04-02 05:25:56

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

要解决这个问题:

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

// ...
 elem[i].addEventListener("click", makeItHappenDelegate(boxa,boxb), false);
嗯,委托是更好的方法,即使在可读性和可维护性方面也是如此。小号奥利德
2021-03-11 05:25:56
这本质上是相同的解决方案。您可以makeItHappenDelegate通过返回一个绑定到boxaand的实际值的函数来创建一个闭包boxb
2021-03-14 05:25:56
是的,但它要好得多,因为我们没有在每个循环上创建自调用函数。是的,我们创建了委托,但这是最小的开销。
2021-03-18 05:25:56
在这种情况下,我怀疑(极少的)性能开销是否超过了可读性的好处。如果您有很长的列表,比如 100.000+,您可能会看到性能优势,但在此之下我真的不会担心。可读性要重要得多。
2021-03-24 05:25:56
我同意,我发现这更具可读性和可维护性。
2021-03-29 05:25:56

你可以使用函数绑定。你不需要使用闭包。见下文:

之前

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);
   }
}
我也喜欢这个。谢谢。
2021-03-16 05:25:56
不错的解决方案,易于实施和工作。您能否就它的工作原理/原因提供更多见解?(我知道我可以搜索它,但如果将来会在这里找到 1 或 2 个段落,那就太好了。)
2021-03-31 05:25:56
我真的不知道这是如何工作的,但它确实如此,而且孩子干净!非常感谢!
2021-04-02 05:25:56
我喜欢这个解决方案。它非常清晰易读。
2021-04-03 05:25:56

这是因为关闭。

看看这个:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

示例代码和你的代码本质上是一样的,对于不知道“闭包”的人来说,这是一个常见的错误。

简而言之,当您在 内部创建处理程序函数时addEvents(),它不仅会iaddEvents()的环境中访问变量,而且还会“记住” i

并且因为您的处理程序“记住” i,变量iaddEvents()执行后不会消失

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

不久前我也遇到了这个问题。我通过在循环外使用“添加”函数来分配事件来解决它,并且它工作得很好。

你的脚本应该看起来像。

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