[英]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”)
我的第一个想法是这是一个范围问题-意味着当我每次创建一个新按钮时,它都会为其他每个单元更新按钮。 我的第一个修复尝试是在主for循环中设置jQuery数据: $(button).data("id", person.id);
,然后将其重新放入click函数: $(button).data("id")
。 不用说,那没有用。
我在做什么错,该如何解决?
这就是作用域在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);
});
编辑:在我看来,您似乎正在尝试向屏幕上添加元素,并且在编写很多凌乱的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.