【问题标题】:Cannot set innerHTML inside setTimeout无法在 setTimeout 内设置 innerHTML
【发布时间】:2020-01-03 01:18:50
【问题描述】:

尝试设置一个 HTML 类的 innerHTML,它们是四个框,每个框依次设置 ​​3 秒。当 innerHTML 设置为加载图标时,我可以在没有 setTimeout 的情况下设置 innerHTML。当将 innerHTML 放入 setTimeout 时,将返回以下内容:'Uncaught TypeError: Cannot set property 'innerHTML' of undefined'。

尝试调试我的代码,将消息发送到控制台并搜索 stackoverflow,但没有成功。

var x = document.getElementsByClassName("numberBox");


for (var i = 0; i < 4; i++) {
    x[i].innerHTML= '';
    x[i].innerHTML= "<div class='loader'></div>"
}

// function to generate array of 4 random numbers
var randomNums = generateRandomNumbers();


for (var i = 0; i < 4; i++) {
    setTimeout(function () {
        x[i].innerHTML= '';
        x[i].innerHTML = randomNums[i];
    }, 3000 * i);
}

想知道为什么我的 innerHTML 不能在此处设置在 setTimeout 内以及我的问题的可能解决方案。

【问题讨论】:

  • 理解 adr5240 指出的闭包和 setTimeout
  • 这些答案的简短版本是 var 很糟糕而且很糟糕,您应该始终改用 let

标签: javascript settimeout typeerror innerhtml


【解决方案1】:

我认为这是当前范围的问题。 setTimeout 函数创建自己的范围,它不引用旧变量。您可能需要重新定义超时内的 x 或将数组显式传递给超时。

请参阅此处了解操作方法:How can I pass a parameter to a setTimeout() callback?

我还建议您阅读闭门器:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

【讨论】:

    【解决方案2】:

    当您在 for 循环中使用 var 时,setTimeout 实际上会为 i 的最后一个值触发,就像在 var 中一样,绑定只发生一次。

    这是因为setTimeout在整个循环完成时被触发,那么你的i将是4。请记住,由于您在 setTimeout 调用中传递的回调函数,所以存在一个闭包。该闭包现在将引用 i 的最终值,即 4。

    因此,在这种情况下,当整个循环执行完毕时,i 的值是 4,但 x 中的索引直到 3。这就是为什么当您尝试访问 x[4] 时,您会看到 undefined 并看到 TypeError 来解决此问题,只需使用 let 在每次迭代中使用 i 的新值重新绑定:

    for (let i = 0; i < 4; i++) {
        setTimeout(function () {
            x[i].innerHTML= '';
            x[i].innerHTML = randomNums[i];
        }, 3000 * i);
    }
    

    此外,如果由于浏览器不兼容而无法使用 let,您可以使用 IIFE 来解决问题:

    for (var i = 0; i < 4; i++) {
          (function(i){
             setTimeout(function () {
               x[i].innerHTML= '';
               x[i].innerHTML = randomNums[i];
             }, 3000 * i);
           })(i);
     }
    

    这是可行的,因为var 具有函数作用域,因此在每次迭代中,都会创建一个新作用域以及与i 的新值绑定的函数。

    【讨论】:

      【解决方案3】:

      由于缺少 i 参数 - 超时可能最后使用 (4) 超出数组。

      但您可以通过添加下一个超时参数来设置和使用函数参数。

      var x = document.getElementsByClassName("numberBox");
      
      
      for (var i = 0; i < 4; i++) {
          x[i].innerHTML= '';
          x[i].innerHTML= "<div class='loader'></div>"
      }
      
      // function to generate array of 4 random numbers
      var randomNums = generateRandomNumbers();
      
      
      for (var i = 0; i < 4; i++) {
          setTimeout(function (i) {
              x[i].innerHTML= '';
              x[i].innerHTML = randomNums[i];
          }, 3000 * i, i);
      }
      
      function generateRandomNumbers() {
      var retVal = [];
      for (var i = 0; i < 4; i++) {
      retVal.push(Math.random());
      }
      return retVal;
      }
      <div class="numberBox"></div>
      <div class="numberBox"></div>
      <div class="numberBox"></div>
      <div class="numberBox"></div>

      【讨论】:

        【解决方案4】:
            // function to generate array of 4 random numbers
            var x = document.getElementsByClassName("numberBox");
            var randomNums = generateRandomNumbers();
            for (var i = 0; i < 4; i++) {
                setTimeout(function () {
                    x[i].innerHTML= '';
                    x[i].innerHTML = randomNums[i];
                }, 3000 * i, x);
            }
        

        【讨论】:

          【解决方案5】:

          这个问题与一个非常基本且流行的 Javascript 概念有关,称为closure。至少可以通过两种方式解决:

          1. 使用let
          var x = document.getElementsByClassName("numberBox");
          
          
          for (let j = 0; j < 4; j++) {
              x[j].innerHTML= '';
              x[j].innerHTML= "<div class='loader'></div>"
          }
          
          // function to generate array of 4 random numbers
          var randomNums = generateRandomNumbers();
          
          
          for (let i = 0; i < 4; i++) {
              setTimeout(function () {
                  x[i].innerHTML= '';
                  x[i].innerHTML = randomNums[i];
              }, 3000 * i);
          }
          
          1. 使用 IIFE
          
          var x = document.getElementsByClassName("numberBox");
          
          
          for (var i = 0; i < 4; i++) {
              x[i].innerHTML= '';
              x[i].innerHTML= "<div class='loader'></div>"
          }
          
          // function to generate array of 4 random numbers
          var randomNums = generateRandomNumbers();
          
          
          for (var i = 0; i < 4; i++) {
              setTimeout((function (j) {
                  x[j].innerHTML= '';
                  x[j].innerHTML = randomNums[j];
              })(i), 3000 * i);
          }
          
          

          【讨论】:

            【解决方案6】:

            您正在尝试在内部方法中访问 x,即 x 未在 setTimeout 的范围内定义,这就是您收到该执行的原因

            我建议您使用setInterval 函数作为解决方案 你的代码:

            var randomNums = generateRandomNumbers(); 
            
            for (var i = 0; i < 4; i++) { 
            
                  setTimeout(function () { 
                     x[i].innerHTML= ''; 
                     x[i].innerHTML = randomNums[i]; 
                 }, 3000 * i); 
            }
            

            解决办法

            var randomNums = generateRandomNumbers();
            let i = 0;
            let interval = setInterval(function() {
                        if( i != 2){
                                x[i].innerHTML= ''; 
                            x[i].innerHTML = randomNums[i];
                                i += 1;
                        } else {
                                clearInterval(interval) ;
                        }
            }, 3000);
            

            【讨论】:

              【解决方案7】:

              这个问题似乎是由几个因素造成的。

              • setTimeout() 范围之外定义generateRandomNumbers() 函数。
              • for 循环中使用var 定义。
              function generateRandomNumbers() {
                  return Math.floor(Math.random() * 999999) + 10000;
              }
              
              var x = document.getElementsByClassName("numberBox");
              
              for (var i = 0; i < x.length; i++) {
                  x[i].innerHTML= '';
                  x[i].innerHTML= "<div class='loader'></div>"
              }
              
              for (let i = 0; i < x.length; i++) {
                  setTimeout(function() {
                    x[i].innerHTML= '';
                    x[i].innerHTML = generateRandomNumbers();
                  }, 3000 * i);
              }
              

              这是一个不同的实现,但这里是 Fiddle

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-06-11
                • 1970-01-01
                • 2015-07-14
                • 1970-01-01
                • 2014-08-25
                相关资源
                最近更新 更多