繁体   English   中英

了解JavaScript列表适配器中的范围

[英]understanding scope in javascript list adapter

我正在动态创建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”

(单击任何单元格以查看警报“所选人员:2”)


我的第一个想法是这是一个范围问题-意味着当我每次创建一个新按钮时,它都会为其他每个单元更新按钮。 我的第一个修复尝试是在主for循环中设置jQuery数据: $(button).data("id", person.id); ,然后将其重新放入click函数: $(button).data("id") 不用说,那没有用。

我在做什么错,该如何解决?

这就是作用域在JavaScript中的工作方式。 诸如iffor这样的循环没有条件,除了深奥的情况,您只有函数范围。

这个问题在初学者中非常普遍, 它在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>

当您编写此代码时:

 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,该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]);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM