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”)


我的第一个想法是这是一个范围问题——这意味着当我每次创建一个新按钮时,它都会更新其他每个单元格的按钮。我的第一次修复尝试是在主 for 循环中设置 jQuery 数据:$(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 的小提琴

编辑:在我看来,您正在尝试向屏幕添加元素,并且在编写大量混乱的 JavaScript、混合 jQuery 和 DOM 方法时遇到了麻烦。如果您有兴趣,这是我将如何解决这个问题:

我将如何编码

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 回答