for文でclick時の動作を定義したい


要素`#elem0` .. `#elem9`に対してクリックイベントを設定し、それぞれの要素に対応した番号を`alert`で出したいと思っています。

下記のコードだと、`#elem0`をクリックしたときには`0`、`#elem1`をクリックしたら`1`、といったようにです。しかし、下のコードだと常に10が表示され、上記のような挙動になりません。どうすればいいのでしょうか?

[code]

    for (var i = 0; i < 10; i++) {
        $('#elem' + i).click(function() {
            alert(i);
        });
    }
    
[/code]




flied onionさんも、Taizan Yonetujiさんも正解を書かれていると思います。  
ここでは後から回答を読まれる方のために少し説明を書きます。

**問題が発生する理由**

問題のコードでは、どのエレメントをクリックしても、alert文が出力するのは"10"です。  
これはJavaScriptのクロージャの仕様によるものです。

単純な例を挙げます：

[code]

    var msg = "foo";
    $('#btn').click(function() {
        alert(msg); // => "bar"
    });
    msg = "bar";
    
[/code]

DEMO :

[code]

    var s = "foo";
    $('#btn').click(function() {
         alert(s);
    });
    s = "bar";
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <input type="button" id="btn" value="click me">
[/code]

この例でalert文が出力するのは、"foo"ではなく"bar"になります。  
`click()`のハンドラ関数がバインドされて実行された時には、変数`msg`の値が"bar"になっているからです。

**即時関数を使った解決法**

解決法のひとつは即時関数を使うものです。即時関数はその名の通り即時に実行されるので、即時関数の中で`var`を使って新規に変数を定義すると、その変数の値は即時関数が定義された時（＝実行された時）のものになります。

[code]

    var msg = "foo";
    (function(){
      var s = msg;
      $('#btn').click(function() {
         alert(s);　// => "foo"
      });
    })();
    msg = "bar";
    
[/code]

DEMO :

[code]

    var msg = "foo";
    
    (function(){
      var s = msg;
      $('#btn').click(function() {
         alert(s);
      });
      
    })();
    msg = "bar";
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <input type="button" id="btn" value="click me">
[/code]

即時関数には引数を渡すこともできるので、同じ内容を以下のように書くこともできます。

[code]

    var msg = "foo";
    (function(s){
        $('#btn').click(function() {
            alert(s); //=> "foo"
        });
    })(msg);
    msg = "bar";
    
[/code]

DEMO :

[code]

    var msg = "foo";
    
    (function(s){
      
      $('#btn').click(function() {
         alert(s);
      });
      
    })(msg);
    msg = "bar";
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <input type="button" id="btn" value="click me">
[/code]

* * *

**forループの問題への回答例**

前置きの説明が長くなりましたが、forループの問題は以下のように書くことで解決できます。

[code]

    for (var i = 0; i < 10; i++) {
        (function (n) {
            $("#elem" + i).click(function(){
                alert(n);
            });
        })(i);
    }
    
[/code]

DEMO :

[code]

    for (var i = 0; i < 10; i++) {
        (function (n) {
            $("#elem" + i).click(function(){
                alert(n);
            });
        })(i);
    }
[/code]

[code]

    div {
        width:100px;
        border:1px gray solid;
        padding:0.5em;
    }
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="elem0">0</div>
    <div id="elem1">1</div>
    <div id="elem2">2</div>
    <div id="elem3">3</div>
    <div id="elem4">4</div>
    <div id="elem5">5</div>
    <div id="elem6">6</div>
    <div id="elem7">7</div>
    <div id="elem8">8</div>
    <div id="elem9">9</div>
[/code]

**即時関数を使わないやりかた**

また、即時関数を使うことが難しい局面では、以下のようにｊQueryの`data()`関数が役に立つ場合もあります。

[code]

    for (var i = 1; i < 10; i++) {
    　　　　$("#elm" + i).data("n",i).click(function () {
        　　　　alert($(this).data('n'))
    　　　　});
    }
    
[/code]

DEMO :

[code]

    for (var i = 0; i < 10; i++) {
      $("#elm" + i).data("n", i).click(function() {
        alert($(this).data('n'));
      });
    }
[/code]

[code]

    div {
      width: 100px;
      border: 1px gray solid;
      padding: 0.5em;
    }
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="elm0">0</div>
    <div id="elm1">1</div>
    <div id="elm2">2</div>
    <div id="elm3">3</div>
    <div id="elm4">4</div>
    <div id="elm5">5</div>
    <div id="elm5">6</div>
    <div id="elm7">7</div>
    <div id="elm8">8</div>
    <div id="elm">9</div>
[/code]

以上です。




flied onionさんも、Taizan Yonetujiさんも正解を書かれていると思います。  
ここでは後から回答を読まれる方のために少し説明を書きます。

**問題が発生する理由**

問題のコードでは、どのエレメントをクリックしても、alert文が出力するのは"10"です。  
これはJavaScriptのクロージャの仕様によるものです。

単純な例を挙げます：

[code]

    var msg = "foo";
    $('#btn').click(function() {
        alert(msg); // => "bar"
    });
    msg = "bar";
    
[/code]

DEMO :

[code]

    var s = "foo";
    $('#btn').click(function() {
         alert(s);
    });
    s = "bar";
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <input type="button" id="btn" value="click me">
[/code]

この例でalert文が出力するのは、"foo"ではなく"bar"になります。  
`click()`のハンドラ関数がバインドされて実行された時には、変数`msg`の値が"bar"になっているからです。

**即時関数を使った解決法**

解決法のひとつは即時関数を使うものです。即時関数はその名の通り即時に実行されるので、即時関数の中で`var`を使って新規に変数を定義すると、その変数の値は即時関数が定義された時（＝実行された時）のものになります。

[code]

    var msg = "foo";
    (function(){
      var s = msg;
      $('#btn').click(function() {
         alert(s);　// => "foo"
      });
    })();
    msg = "bar";
    
[/code]

DEMO :

[code]

    var msg = "foo";
    
    (function(){
      var s = msg;
      $('#btn').click(function() {
         alert(s);
      });
      
    })();
    msg = "bar";
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <input type="button" id="btn" value="click me">
[/code]

即時関数には引数を渡すこともできるので、同じ内容を以下のように書くこともできます。

[code]

    var msg = "foo";
    (function(s){
        $('#btn').click(function() {
            alert(s); //=> "foo"
        });
    })(msg);
    msg = "bar";
    
[/code]

DEMO :

[code]

    var msg = "foo";
    
    (function(s){
      
      $('#btn').click(function() {
         alert(s);
      });
      
    })(msg);
    msg = "bar";
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <input type="button" id="btn" value="click me">
[/code]

* * *

**forループの問題への回答例**

前置きの説明が長くなりましたが、forループの問題は以下のように書くことで解決できます。

[code]

    for (var i = 0; i < 10; i++) {
        (function (n) {
            $("#elem" + i).click(function(){
                alert(n);
            });
        })(i);
    }
    
[/code]

DEMO :

[code]

    for (var i = 0; i < 10; i++) {
        (function (n) {
            $("#elem" + i).click(function(){
                alert(n);
            });
        })(i);
    }
[/code]

[code]

    div {
        width:100px;
        border:1px gray solid;
        padding:0.5em;
    }
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="elem0">0</div>
    <div id="elem1">1</div>
    <div id="elem2">2</div>
    <div id="elem3">3</div>
    <div id="elem4">4</div>
    <div id="elem5">5</div>
    <div id="elem6">6</div>
    <div id="elem7">7</div>
    <div id="elem8">8</div>
    <div id="elem9">9</div>
[/code]

**即時関数を使わないやりかた**

また、即時関数を使うことが難しい局面では、以下のようにｊQueryの`data()`関数が役に立つ場合もあります。

[code]

    for (var i = 1; i < 10; i++) {
    　　　　$("#elm" + i).data("n",i).click(function () {
        　　　　alert($(this).data('n'))
    　　　　});
    }
    
[/code]

DEMO :

[code]

    for (var i = 0; i < 10; i++) {
      $("#elm" + i).data("n", i).click(function() {
        alert($(this).data('n'));
      });
    }
[/code]

[code]

    div {
      width: 100px;
      border: 1px gray solid;
      padding: 0.5em;
    }
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="elm0">0</div>
    <div id="elm1">1</div>
    <div id="elm2">2</div>
    <div id="elm3">3</div>
    <div id="elm4">4</div>
    <div id="elm5">5</div>
    <div id="elm5">6</div>
    <div id="elm7">7</div>
    <div id="elm8">8</div>
    <div id="elm">9</div>
[/code]

以上です。

