for(var i = 1 ; i <= 10 ; i ++) { // 注意循环变量 i var newLine = "<tr>"; newLine += "<td><input type='text' id='sampleInput" + i + "'></td>"; newLine += "<td><span id='sampleSpan" + i + "'></td>"; newLine += "</tr>"; // 动态为 newLine 追加 HTML,id 与循环变量有关 $("#sampleTable tbody").append(newLine); // 往已存在的 table 中添加新的这一行 $("#sampleInput" + i).on("input", function(){ //这里设置的监听对象是没问题的,能够实现对于 sampleInput i 的监听 $.ajax({ type: "get", url: "sampleAction", // data: {}, 假装这里有传别的值 success: function(data){ // data = eval("(" + data + ")"); $("#sampleSpan" + i).html(data); // 这里想修改 sampleSpan i 的值 } }); }); }
上面这一段 \(JS\) 代码,意图是往 \(sampleTable\) 中插入十行,每行一个文本框 \(sampleInput\{i\}\) 和一个行内元素 \(sampleSpan\{i\}\)
然后对于每行的文本框,只要触发了 \(input\) 事件就向后台发送请求,将获取到的数据在同一行的行内元素上显示出来
但这段代码的事实却是,不论修改哪个文本框,行内元素都没有效果
实际上在 \(ajax\) 的 \(success\) 匿名函数里看看 \(i\) 的值就清楚了,加一行 \(alert\) 输出,发现所有 \(i\) 的值都是 \(11\)
这是因为 \(ajax\) 只有执行完请求且获得数据后才会执行 \(success\) 匿名函数,这一部分与循环是异步执行的,也就是说在执行这个函数的时候可能外头的 \(for\) 循环已经结束了(终值就是 \(11\))
而在函数内调用 \(i\) 这个变量是通过地址去取值的,并没有把执行 \(ajax\) 时的 \(i\) 的值缓存下来,才会出现上面的这种情况
这里可能有人会说,我直接把 \(ajax\) 的异步关了就是了 async: false
所以上面的例子里在外层套了个监听
监听事件触发的时候循环肯定是执行完了的,与这个异步没有关系
解决方法,把需要的值通过 \(ajax\) 传给后端,让后端重新传回来,问题解决(简单粗暴)
因为传入值时还没有开始异步执行,引用的 \(i\) 是会缓存当时的值的
for(var i = 1 ; i <= 10 ; i ++) { var newLine = "<tr>"; newLine += "<td><input type='text' id='sampleInput" + i + "'></td>"; newLine += "<td><span id='sampleSpan" + i + "'></td>"; newLine += "</tr>"; $("#sampleTable tbody").append(newLine); $("#sampleInput" + i).on("input", function(){ $.ajax({ type: "get", url: "sampleAction", data: {id: i}, // 把需要的值 i 传给后端 success: function(data){ data = eval("(" + data + ")"); var i = data.id; // 让 action 再把它返回来就是了 $("#sampleSpan" + i).html(data.data); } }); }); }
后记:
挺奇妙的,在 debug 的时候竟然没有遇到“无法找到动态添加的元素”的问题
本来我是往这个方向想的,于是就舍弃了通过 id 来查找元素,换了其他麻烦的方法(相对位置检索大法)
然后重新回来看这个问题,越看越奇怪,于是输出了一下循环变量,直接豁然开朗……