【问题标题】:Why is the loop assigning a reference of the last index element to? [duplicate]为什么循环将最后一个索引元素的引用分配给? [复制]
【发布时间】:2014-02-24 13:37:42
【问题描述】:

我想为我的所有标签添加一个事件侦听器,每个标签都传递一个对自身的引用当触发事件时作为参数。这是我写的函数:

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

该函数基本上应该做以下事情:

  1. 将具有指定类名的所有输入标签存储在一个数组中
  2. 循环遍历数组,为每个标签添加一个 id 和
  3. 使用isNumberOrDot(event, tagId) 处理程序添加onkeyup 事件侦听器。

问题

添加了 onkeyup 事件,但每个事件的处理程序总是引用数组最后一个元素的tagId

问题

代码/逻辑有什么问题?又该如何解决?

注意

当然,这个问题与循环中的 JavaScript 闭包有关,虽然这个问题可能有更一般的答案,但它特定于正在使用的事件侦听器。对于更高级的开发人员,可能很容易将通用解决方案应用于此问题。但对我来说,其他解决方案仍然没有提供完整的解释,甚至没有奏效。

提前谢谢你。

【问题讨论】:

  • 这是因为您正在处理关闭问题。
  • 这个问题已经被问了十亿次。如果要在回调中访问,您需要在闭包中为每个循环迭代绑定变量的值。
  • 感谢您为我指明了正确的方向。我不知道使用哪些关键词来找到解决方案。得到答案后,我会正确地将我的问题标记为重复。
  • @andrew 这不是很精确。事件处理程序已经是一个闭包。解决方案是通过执行一个函数来创建一个新的作用域(这是否涉及另一个闭包并不重要)。

标签: javascript arrays dom for-loop


【解决方案1】:

因为实际事件发生在您的 for 循环已经完成运行之后的某个时间,因此它的索引处于最后一个值,并且您的函数中的任何局部变量(如 tagId)也处于它们的最后一个值。您需要创建某种闭包,为​​每个事件处理程序唯一地保留 itagId 的值,以便它们每个都可以访问自己的值。

有几种不同的方法可以做到这一点,但都涉及将i 值传递给每个事件处理程序的函数。

这是一个使用 IIFE(立即调用函数表达式)的例子:

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 循环中的索引传递到闭包中,并在事件处理程序中基于它进行任何计算(尽管任何一种方法都可以正常工作),如下所示:

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

【讨论】:

  • 这就是我需要的解决方案。谢谢你的解释。太糟糕了,这个问题被标记为其他问题的重复,而这些问题的答案在这种特定情况下没有提供足够的信息。即使他们针对的是同一个主题。谢谢。
  • @sargas - 是的,这是一个常见的根本问题,被问了很多,但最好的解决方案通常会因不同的情况而有所不同,所以当“重复”真的是最好的时候并不总是很明显回答问题的方法。无论如何,很高兴您现在知道如何解决您的问题。
猜你喜欢
  • 2020-01-07
  • 1970-01-01
  • 2013-12-16
  • 1970-01-01
  • 1970-01-01
  • 2013-12-15
  • 1970-01-01
  • 2016-07-14
  • 1970-01-01
相关资源
最近更新 更多