0

のリストを動的に作成し、buttonsそれらをに追加していspanます。各ボタンは、ajax 経由でダウンロードされた JSON データに関連付けられています。データは明らかに機能していますが、jQuery clickイベントが正しいインデックス データを取得していません。つまり、そのセルに関連付けられるべきデータはそうではなく、クリックしたセルは実際にはリストの最後の要素のデータに対応するだけです。より明確にするために、ここに私が持っているものがあります(またはここで利用可能なフィドル):

  • JSON データ

    var data = [{"id":0, "name":"Phil"}, {"id":1, "name":"Ed"}, {"id":2, "name":"Fred"}];
    
  • HTML

    <span id="output"></span><br>
    
  • CSS

    .listButton {
        color: #E3A869;
        font: 12pt sans-serif;
        display: block; 
        width: 100%;
        background: white;
        border: 1px solid #FF7F00;
        text-align: left;
        cursor: pointer;
    }
    
  • コード

    for (var i = 0; i < data.length; i++) {
        var person = data[i];
        var button = document.createElement('button');
        button.type = 'button';
        $(button).addClass('listButton');
        var text = "<b>ID:</b> " + person.id + "<br>";
        text = text + "<b>Name:</b> " + person.name;
        $(button).html(text);
        $(button).click(function() {
            alert("selected person: " + person.id);//always 2, no matter who is clicked.
        });
        document.getElementById("output").appendChild(button);
    }
    
  • 結果

アラートを表示するには、任意のセルをクリックします

(任意のセルをクリックすると、「選択された人物: 2」というアラートが表示されます)


私の最初の考えは、これはスコープの問題であるということです。つまり、毎回新しいボタンを作成すると、他の各セルのボタンが更新されます。修正のための私の最初の試みは、jQuery データをメインの for loop: に設定し$(button).data("id", person.id);、それを click 関数内に戻すこと$(button).data("id")でした。言うまでもなく、それはうまくいきませんでした。

私は何を間違っていますか、どうすれば修正できますか?

4

2 に答える 2

3

これが JavaScript でジャスト スコープが機能する方法です。のような条件ifとループのようなものforにはスコープがありません。難解なケースを除いて、関数のスコープしかありません。

この問題は初心者の間で非常に一般的であるため、MDN のガイドのクロージャーに関する特別なセクションがあります。

ループ スコープはありません。アラートは にバインドされてpersonおり、ループの最後では第三者です。

各ハンドラーが対応する人に反応するようにするには、イベント ハンドラーでその人を「閉じる」必要があります。

(function(person){
    $(button).click(function() {
            alert("selected person: " + person.id);//correct value!
    });
})(person); // close over the person

ハンドラーをラップする関数を作成しています。渡された値を追跡するpersonため、ハンドラーは実行する値を認識します。

別のアプローチは、Array.forEach代わりに使用することです。これは、配列内のそれぞれに対して関数を実行しperson、効果的にスコープを作成します。

これは次のようになります。

data.forEach(function(person){
    var button = document.createElement('button');
    button.type = 'button';
    $(button).addClass('listButton');
    var text = "<b>ID:</b> " + person.id + "<br>";
    text = text + "<b>Name:</b> " + person.name;
    $(button).html(text);
    $(button).click(function() {
        alert("selected person: " + person.id);
    });
    document.getElementById("output").appendChild(button);
});

フィドル

jQueryなしでフィドル

編集:画面に要素を追加しようとしていて、jQuery と DOM メソッドを混在させて、面倒な JavaScript をたくさん書くのに苦労しているように思えます。興味がある場合は、これを解決する方法を次に示します。

どのようにコーディングするか

JS:

var data = [{
    "id": 0,
    "name": "Phil"
}, {
    "id": 1,
    "name": "Ed"
}, {
    "id": 2,
    "name": "Fred"
}];


ko.applyBindings(new ViewModel(data));

function ViewModel(model){
    this.buttonList = model;
    this.showSelected = function(element){
        alert("Selected person: "+element.id);
    }
}

HTML:

<span id="output"></span>
<div data-bind="foreach: buttonList">
    <button class='listButton' data-bind="click: $root.showSelected">
        <b>ID:</b> <span data-bind="text:id"></span><br />
        <b>Name:</b> <span data-bind="text:name"></span>
    </button>
</div>
于 2013-05-28T18:23:17.817 に答える
1

これを書くとき:

 function() {
   for (var i = 0; i < 10; i++) {
      var k = i;
   }
 }

あなたは実際にこれを書きます:

function() {
   var k, i;
   for (i=0; i<10; i++) {
      k=i;
   }
}

次に、変数をパラメーターとして関数に渡すと、すべてを参照渡しする JavaScript によって混乱します。簡単な解決策は、次のようなパターンです。

  for (var i = 0 ; i < 10 ; i++) {
     (function(current) {
       // Your code here
     })(i);
  }

そこにある関数には、実行された瞬間のを取得するように設定されたパラメーターを持つ IETF があります。実際の実行ごとの値がiあることを確認できます。current

修正されたコード:

for (var i = 0; i < data.length; i++) {
 (function(person) {
  var button = document.createElement('button');
  button.type = 'button';
  $(button).addClass('listButton');
  var text = "<b>ID:</b> " + person.id + "<br>";
  text = text + "<b>Name:</b> " + person.name;
  $(button).html(text);
  $(button).click(function() {
    alert("selected person: " + person.id);//always 2, no matter who is clicked.
  });
  document.getElementById("output").appendChild(button);
 })(data[i]);
}
于 2013-05-28T18:25:48.517 に答える