为什么循环将最后一个索引元素的引用分配给?
我想添加一个事件监听器到我所有的标签,当even被触发时, 每个标签都传递一个引用作为参数。 这是我写的function:
function validateDigitsFeature() { // Add the event listeners to input tags // Get the array of input tags var inputTags = document.getElementsByClassName('validateInput'); var tagId; // Loop through them, adding the onkeypress event listener to each one for (var i = 0; i < inputTags.length; i++) { // Give each input element an id tagId = inputTags[i].id = 'input_id_' + i; inputTags[i].addEventListener('keyup', function(){isNumberOrDot(event, tagId);}, false); } }
基本上function应该做到以下几点:
- 将所有具有指定类名的input标签存储在一个数组中
- 循环访问数组,为每个标签添加一个id
- 使用
isNumberOrDot(event, tagId)
处理程序添加onkeyup
事件侦听器。
问题
onkeyup事件被添加,但是它们的处理程序总是引用数组最后一个元素的tagId
。
题
代码/逻辑有什么问题? 而且如何解决?
注意
当然,这个问题与循环中的JavaScript Closure有关,而这个问题可能会有一个更为一般的答案,它是特定于正在使用的事件监听器的。 对于更高级的开发人员来说,将这个通用的解决scheme应用于这个问题可能很容易。 但对我来说,其他解决scheme仍然没有提供完整的解释,甚至没有工作。
先谢谢你。
因为在for
循环已经完成运行之后,实际的事件会在将来某个时候发生,因此它的索引是最后一个值,并且像tagId
这样的函数中的任何局部variables也都是最后一个值。 你需要创build一些闭包来保存每个事件处理程序唯一的i
或tagId
的值,这样它们都可以访问自己的值。
有几种不同的方法可以做到这一点,但都包括将i
值传递给每个事件处理函数的函数。
这是一个使用IIFE(立即调用的函数expression式):
function validateDigitsFeature() { // Add the event listeners to input tags // Get the array of input tags var inputTags = document.getElementsByClassName('validateInput'); // Loop through them, adding the onkeypress event listener to each one for (var i = 0; i < inputTags.length; i++) { // Give each input element an id (function() { // creates a unique function context for each event handler so the // value of tagId is unique for each event handler var tagId = inputTags[i].id = 'input_id_' + i; inputTags[i].addEventListener('keyup', function(){isNumberOrDot(event, tagId);}, false); })(); } }
一个更常见的方法是将这个索引从for
循环传入到闭包中,并在事件处理函数中根据它进行任何计算(尽pipe任何方法都可以正常工作),如下所示:
function validateDigitsFeature() { // Add the event listeners to input tags // Get the array of input tags var inputTags = document.getElementsByClassName('validateInput'); // Loop through them, adding the onkeypress event listener to each one for (var i = 0; i < inputTags.length; i++) { // Give each input element an id (function(index) { // passes the `for` loop index into a function closure // so it is uniquely preserved for each event handler inputTags[index].addEventListener('keyup', function(){ isNumberOrDot(event, inputTags[index].id = 'input_id_' + index); }, false); })(i); } }