1. ホーム
  2. javascript

[解決済み] addEventListenerのforループと値渡し [重複].

2022-12-17 05:36:19

質問

forループを使って複数のオブジェクトにイベントリスナーを追加しようとしているのですが、結局すべてのリスナーが同じオブジェクト --> をターゲットにしています。

インスタンスごとにboxaとboxbを定義してリスナーを手動で追加すると、それは動作します。私が期待した方法で動作していないのは、addEvent for-loop のせいだと思います。たぶん、私はまったく間違ったアプローチを使用しています。

class="container" の 4 つを使用した例です。 コンテナ 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>

どのように解決するのですか?

クロージャ! :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 , if , while など) は関数の先頭に移動されます。 更新します。 ES6では let を使ってブロックスコープの変数を取得することができます。

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

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


のようなコード解析ツールは JSLint または JSHint を使えば、このような怪しいコードをキャッチすることができます。もしあなたがたくさんのコードを書いているのなら、これらのツールの使い方を学ぶために時間をかける価値はあるでしょう。IDEによっては、これらのツールが組み込まれているものもあります。