104

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>

4

5 に答える 5

153

閉鎖!: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 にはブロック スコープ ( 、 など) がないforためifwhile関数の先頭に移動されます。更新: ES6 の時点で、letブロック スコープの変数を取得するために使用できます。

コードを実行すると、次のことが起こります。forループ内でクリック コールバックを追加して を割り当てますboxaが、その値は次の繰り返しで上書きされます。クリック イベントが発生するとコールバックが実行され、 の値boxa常にリストの最後の要素になります。

クロージャー ( などの値を閉じる) を使用してboxaboxb値をクリック ハンドラーのスコープにバインドします。


JSLintJSHintなどのコード分析ツールは、このような疑わしいコードをキャッチできます。大量のコードを作成している場合は、時間をかけてこれらのツールの使用方法を学習する価値があります。一部の IDE にはそれらが組み込まれています。

于 2013-10-25T09:34:43.137 に答える
15

function(){makeItHappen(boxa,boxb);} boxaおよびboxb参照としてスコープ/クロージャーの問題に直面し、常に最後の1つの要素を参照します。

この問題を解決するには:

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

// ...
 elem[i].addEventListener("click", makeItHappenDelegate(boxa,boxb), false);
于 2013-10-25T09:37:49.107 に答える
14

関数バインディングを使用できます。クロージャーを使用する必要はありません。以下を参照してください。

:

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);
   }
}
于 2016-08-09T20:47:38.877 に答える
1

私も少し前にこの問題を抱えていました。ループの外側で「追加」機能を使用してイベントを割り当てることで解決しましたが、完全に機能しました。

スクリプトは次のようになります。

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

于 2016-01-10T01:52:18.700 に答える