JavaScriptで次のようなプログラムを作成しました。


for (i=0; i<5; i++) {
  document.getElementById('x'+i).onclick = function() {
    xxx(i);
  }
}

これで、5個の似たようなボタンにイベントを割り当て、ボタンごとにidを変えて動作させたいのですが、正常に動作しません。

イベント発生時の呼び出しが、その都度評価されるようで、必ず最後の値で(ここでは 6)で呼び出されてしまうのです。これを、きちんと 1, 2, 3, 4, 5の値でそれぞれ呼び出すようにするにはどうしたらよいでしょうか? 教えてください。

回答の条件
  • 1人2回まで
  • 登録:2008/10/02 18:09:54
  • 終了:2008/10/02 18:46:39

ベストアンサー

id:Numeric No.2

Numeric回答回数83ベストアンサー獲得回数182008/10/02 18:26:26

ポイント35pt

xxx(i)の引数iのスコープの問題です。(グローバル変数になっています)

解決策として、たとえば以下のようにメンバー変数にしてしまう方法があります。

for (i=0; i<5; i++) {
  document.getElementById('x'+i).indexNo = i;
  document.getElementById('x'+i).onclick = function() {
    xxx(this.indexNo);
  }
}
id:selter

ありがとうございます。その方法でばっちり解決することができました。

誠にありがとうございました!

2008/10/02 18:43:14

その他の回答(2件)

id:GoldenDawn No.1

GoldenDawn回答回数426ベストアンサー獲得回数812008/10/02 18:22:16

ポイント23pt

そのコードだと 0~4 になると思いますが。


例えばこんな感じとか

for (i=0; i<5; i++) {
  eval("\
    document.getElementById('x'+i).onclick = function() {\
      xxx("+i+");\
    }\
  ") ;
}
id:selter

ありがとうございます!

今回は、他の方のコードを参考にさせていただきましたが、知識として大変参考になりました。ありがとうございました!

2008/10/02 18:42:56
id:Numeric No.2

Numeric回答回数83ベストアンサー獲得回数182008/10/02 18:26:26ここでベストアンサー

ポイント35pt

xxx(i)の引数iのスコープの問題です。(グローバル変数になっています)

解決策として、たとえば以下のようにメンバー変数にしてしまう方法があります。

for (i=0; i<5; i++) {
  document.getElementById('x'+i).indexNo = i;
  document.getElementById('x'+i).onclick = function() {
    xxx(this.indexNo);
  }
}
id:selter

ありがとうございます。その方法でばっちり解決することができました。

誠にありがとうございました!

2008/10/02 18:43:14
id:Mars No.3

Mars回答回数203ベストアンサー獲得回数202008/10/02 18:30:24

ポイント23pt
document.getElementById('x'+i).onclick = Function('xxx('+i+')');

とか、少し変わっちゃうけど、

document.getElementById('x'+i).onclick = function(){xxx(this.id)};

とか。

前者はFunction関数で文字列を関数として定義してます。

後者はオブジェクトのid('x0'~'x4')を渡しています。(関数xxxも改造が必要)

id:selter

ありがとうございます!

今回は、他の方のコードを参考にさせていただきましたが、知識として大変参考になりました。ありがとうございました!

2008/10/02 18:43:29
  • id:practicalscheme
    Numericさんの回答で良いのですが、イメージとして補足するなら、こんな感じで考えてみてはどうでしょう。

    (1) 'i' という変数がひとつあり、for文を回すにつれ0から5まで変化します。
    (2) for文の一回ごとに、function式が評価されます (イベント発生時に評価されるわけではありません。) function式はその場で関数を作って返しますが、その時に上の'i'という変数への参照を持ちます。
    (3) ボタンがクリックされた際に、(2)で作られた関数が呼び出されます。その中で変数'i'が参照されます。しかしこの時点でfor文の実行は終わっていて、iの値はfor文を抜けた時点の値(i=5)となっているので、その値が使われます。

    これはiがグローバル変数でなくても (for文自体が他の関数の中に入っていて、iがその関数のローカル変数でも)変わりません。


    一番のポイントは、(2)の時点でfunctionの中身はその時の'i'の値ではなく変数'i'への参照 (のようなもの) を持つということです。


    逆に、所望の動作をさせるにはfunctionを作る時点でのiの値をどうにかして取っておけば良いということになります。Numericさんはメンバ変数でそれを実現していましたが、次のように関数を使う手もあります。make_callbackを呼ぶ時点でiの値が取られ、make_callbackはその値を(nとして)埋め込んだ関数を返します。


    function make_callback(n) { return function () { xxx(n); }};

    for (i=0; i<5; i++) {
    document.getElementById('x'+i).onclick = make_callback(i);
    }


    今回の例ではNumericさんの方がわかりやすいし簡潔ですが、例えば取っておきたい変数がたくさんある場合だとか、似たようなコールバック関数をたくさん書かないとならない場合にはこのように関数で共通化する方が簡潔になる場合もあります。

    ここに出てきたように、自分自身のfunctionの外側の変数への参照を持つ関数をクロージャと言います。クロージャは最近のプログラムではJavaScriptに限らず良く使われるようになってきていますが、「値ではなく、変数への参照を持っているんだ」という点を覚えておけば動作を理解しやすいだろうと思います。
  • id:selter
    詳しい説明ありがとうございます。
    何となく、参照を保持してしまっているのであろうことは理解できていたのですが、より明確になりました。

    参考にさせていただきますー。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません