简体   繁体   English

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

[英]understanding scope in javascript list adapter

I am dynamically creating a list of buttons and appending them to a span . 我正在动态创建buttons列表,并将它们附加到span Each button is associated with JSON data that has been downloaded via ajax. 每个按钮都与通过ajax下载的JSON数据相关联。 The data is obviously working, but my jQuery click events are not getting the correct index data. 数据显然可以正常工作,但是我的jQuery click事件未获取正确的索引数据。 That is, the data that should be associated with that cell is not - and any cell that I click will actually just correspond to the data for the last element in the list. 也就是说,应该与该单元格关联的数据不是-我单击的任何单元格实际上都只对应于列表中最后一个元素的数据。 To be clearer, here is what I have (or fiddle available here ): 更清楚地说,这是我所拥有的(或在这里提供小提琴):

  • The JSON data JSON数据

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

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

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

     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); } 
  • The Result 结果

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

(Click any cell to see the alert "selected person: 2") (单击任何单元格以查看警报“所选人员:2”)


My first thought is that this is a scope issue - meaning that when I create a new button each time, it is updating the button for each of the other cells. 我的第一个想法是这是一个范围问题-意味着当我每次创建一个新按钮时,它都会为其他每个单元更新按钮。 My first attempt for a fix was to set jQuery data in the main for loop: $(button).data("id", person.id); 我的第一个修复尝试是在主for循环中设置jQuery数据: $(button).data("id", person.id); , then getting it back inside the click function: $(button).data("id") . ,然后将其重新放入click函数: $(button).data("id") Needless to say, that did not work. 不用说,那没有用。

What am I doing wrong, and how can I fix it? 我在做什么错,该如何解决?

This is how just scoping works in JavaScript. 这就是作用域在JavaScript中的工作方式。 Conditions like if and loops like for don't have scope, except for esoteric cases, you only have function scope. 诸如iffor这样的循环没有条件,除了深奥的情况,您只有函数范围。

This problem is so common among beginners, it has a special section in MDN's guide on closures . 这个问题在初学者中非常普遍, 它在MDN的闭包指南中有一个特殊的部分

You have no loop scope, your alert is bound to person , which at the end of the loop is the third person. 您没有循环作用域,您的警报已绑定到person ,在循环末尾是第三人称。

You need to "close over" the person in your event handler if you'd like each handler to react to the corresponding person. 如果您希望每个处理程序对相应的人员做出反应,则需要“关闭”事件处理程序中的人员。

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

We are creating a function that wraps our handler, it keeps track of the value of person that was passed to it, so the handler knows what value to run on. 我们正在创建一个包装处理程序的函数,它跟踪传递给它的person的值,因此处理程序知道要在其上运行的值。

An alternative approach, would be using Array.forEach instead, which would run a function over each person in the array, effectively creating scope. 另一种方法是使用Array.forEach ,它将对数组中的每个person运行一个函数,从而有效地创建范围。

This would be something like: 就像这样:

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);
});

Fiddle 小提琴

Fiddle without jQuery 没有jQuery的小提琴

EDIT: It seems to me like you're trying to add elements to the screen and having trouble writing a lot of messy JavaScript, mixing jQuery and DOM methods. 编辑:在我看来,您似乎正在尝试向屏幕上添加元素,并且在编写很多凌乱的JavaScript,混合jQuery和DOM方法时遇到麻烦。 If you're interested, here is how I would solve this: 如果您有兴趣,请按以下方法解决:

How I'd code it 我怎么编码

JS: 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: 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>

When you write this: 当您编写此代码时:

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

You actually write this: 您实际上是这样写的:

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

When you then pass the variable as a parameter to a function, you get screwed over by JavaScript passing everything by reference. 然后,当您将变量作为参数传递给函数时,JavaScript会通过引用传递所有内容,从而给您带来麻烦。 An easy solution is a pattern similar to this: 一个简单的解决方案是类似于以下模式:

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

the function in there is an IETF with a parameter set to get the value of i the moment it is executed. 该函数中有一个IETF,该IETF的参数设置为在执行时获取i You can be sure that current in there is your actual per-run value. 您可以确定其中的current值是您的实际每次运行值。

Your corrected code: 您更正的代码:

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