5

イベントリスナーを使用して、onclick関数を使用してdiv内のdivでイベントがバブリングするのを防ぎたいです。これは機能し、意図したとおりにパラメーターを渡します。

<div onclick="doMouseClick(0, 'Dog', 'Cat');" id="button_id_0"></div>
<div onclick="doMouseClick(1, 'Dog', 'Cat');" id="button_id_1"></div>
<div onclick="doMouseClick(2, 'Dog', 'Cat');" id="button_id_2"></div>

<script>
function doMouseClick(peram1, peram2, peram3){
    alert("doMouseClick() called AND peram1 = "+peram1+" AND peram2 = "+peram2+" AND peram3 = "+peram3);
}
</script>

ただし、次の方法でループ内に複数のイベントリスナーを作成しようとしました。

<div id="button_id_0"></div>
<div id="button_id_1"></div>
<div id="button_id_2"></div>

<script>
function doMouseClick(peram1, peram2, peram3){
    alert("doMouseClick() called AND peram1 = "+peram1+" AND peram2 = "+peram2+" AND peram3 = "+peram3);
}

var names = ['button_id_0', 'button_id_1', 'button_id_2'];

    for (var i=0; i<names.length; i++){

        document.getElementById(names[i]).addEventListener("click", function(){
        doMouseClick(i, "Dog", "Cat");

    },false);

}

</script>

クリック関数を各divに正しく割り当てますが、各divの最初のパラメーターperam1はです3i私は、3つの異なるイベントハンドラーがすべてforの異なる値を渡すことを期待していましたperam1

なぜこうなった?イベントハンドラーはすべて別々ではありませんか?

4

5 に答える 5

20

問題はクロージャです。JSにはブロックスコープがないため(関数スコープのみ)i、イベント関数が別のスコープを作成するため、使用するまでにループiからの最新の値になっているため、考えているものではありません。forの値を保持する必要がありますi

IIFEの使用:

for (var i=0; i<names.length; i++) {
  (function(i) {
    // use i here
  }(i));
}

使用forEach

names.forEach(function( v,i ) {
  // i can be used anywhere in this scope
});
于 2013-01-06T00:04:31.680 に答える
2

すでに指摘したように、問題はクロージャと可変スコープに関係しています。正しい値が渡されるようにする1つの方法は、変数を正しいスコープ内に保持して、目的の関数を返す別の関数を作成することです。jsfiddle

var names = ['button_id_0', 'button_id_1', 'button_id_2'];

function getClickFunction(a, b, c) {
  return function () {
    doMouseClick(a, b, c)
  }
}
for (var i = 0; i < names.length; i++) {
  document.getElementById(names[i]).addEventListener("click", getClickFunction(i, "Dog", "Cat"), false);
}

そして、代わりにオブジェクトを使用してこれを行うことができる1つの方法を説明するために:

var names = ['button_id_0', 'button_id_1', 'button_id_2'];

function Button(id, number) {
  var self = this;
  this.number = number;
  this.element = document.getElementById(id);
  this.click = function() {
    alert('My number is ' + self.number);
  }
  this.element.addEventListener('click', this.click, false);
}
for (var i = 0; i < names.length; i++) {
  new Button(names[i], i);
}

または少し異なる:

function Button(id, number) {
  var element = document.getElementById(id);
  function click() {
    alert('My number is ' + number);
  }
  element.addEventListener('click', click, false);
}
for (var i = 0; i < names.length; i++) {
  new Button(names[i], i);
}
于 2013-01-06T01:27:42.930 に答える
1

それは閉鎖のためです。

これをチェックしてください:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

サンプルコードとコードは基本的に同じです。「クロージャ」を知らない人にとってはよくある間違いです。

簡単に言うと、ハンドラー関数を作成するとi、外部環境から変数にアクセスするだけでなく、「記憶」することもできますi

したがって、ハンドラーが呼び出されると、ハンドラーが使用されますiが、変数iはforループの後に2になります。

于 2016-06-24T15:12:46.267 に答える
1

私はこの問題に数時間苦労してきましたが、今ではなんとか解決できました。関数コンストラクターを使用した私の解決策は次のとおりです。

function doMouseClickConstructor(peram1, peram2, peram3){
    return new Function('alert("doMouseClick() called AND peram1 = ' + peram1 + ' AND peram2 = ' + peram2 + ' AND peram3 = ' + peram3 + ');');
}

for (var i=0; i<names.length; i++){
    document.getElementById(names[i]).addEventListener("click", doMouseClickConstructor(i,"dog","cat"));
};

注:私は実際にこのコードをテストしていません。しかし、私はすべての重要なことを行うこのcodepenをテストしたので、上記のコードが機能しない場合は、おそらくスペルミスをしただけです。コンセプトはまだ機能するはずです。

ハッピーコーディング!

于 2020-06-24T14:51:23.737 に答える
0

javascriptではすべてがグローバルです。ループの後で3に設定されている変数iを呼び出しています...ループiの後で1000に設定すると、各メソッド呼び出しで1000forが生成されiます。

状態を維持したい場合は、オブジェクトを使用する必要があります。クリックメソッドに割り当てるコールバックメソッドをオブジェクトに持たせます。

イベントバブリングのためにこれを行うとおっしゃいました...イベントバブリングを停止するために、それは言語に組み込まれているので、実際には必要ありません。イベントのバブリングを防ぎたい場合は、コールバックに渡されたオブジェクトのstopPropagation()メソッドを使用する必要があります。event

function doStuff(event) {
    //Do things
    //stop bubbling
    event.stopPropagation();
}
于 2013-01-06T00:03:18.163 に答える