简体   繁体   English

无法在setTimeout中设置innerHTML

[英]Cannot set innerHTML inside setTimeout

Trying to set the innerHTML of a HTML class which are four boxes, each to be set 3 seconds one after another. 试图设置一个HTML类的innerHTML,它们是四个盒子,每个盒子一个接一个地设置3秒。 I can set the innerHTML without setTimeout when the innerHTML is set to a loading icon. 当innerHTML设置为加载图标时,我可以在不使用setTimeout的情况下设置innerHTML。 When innerHTML is put inside setTimeout the following is returned: 'Uncaught TypeError: Cannot set property 'innerHTML' of undefined'. 当innerHTML放在setTimeout中时,返回以下内容:'Uncaught TypeError:无法设置undefined'的属性'innerHTML'。

Tried to debug my code sending messages to the console and searching stackoverflow but no luck. 试图调试我的代码发送消息到控制台和搜索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);
}

Would like to know why my innerHTML cannot be set within setTimeout here and possible solutions to my problem. 想知道为什么我的innerHTML不能在setTimeout中设置这里以及我的问题的可能解决方案。

I believe this is a question of the current scope. 我认为这是目前范围的问题。 The setTimeout function creates its own scope that has no reference to the old variable. setTimeout函数创建自己的范围,该范围没有引用旧变量。 You'll likely need to redefine what x is inside the timeout or pass the array explicitly to the timeout. 您可能需要重新定义超时内的x或将数组显式传递给超时。

See here for how-to: How can I pass a parameter to a setTimeout() callback? 请参阅此处了解操作方法: 如何将参数传递给setTimeout()回调?

I would also recommend reading up on closers as well: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures 我还建议你阅读关闭器: https//developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

When you use var inside the for-loop the setTimeout is actually triggered for the last value of i as in var the binding happens only once. 当您使用var for循环里面setTimeout实际上是触发的最后一个值ivar绑定只发生一次。

This is because the setTimeout is triggered when the entire loop is completed, then your i will be 4 . 这是因为当整个循环完成时会触发setTimeout ,然后你的i将为4 Keep in mind that there is a closure because of the callback function you pass in the setTimeout call. 请记住,由于您在setTimeout调用中传递的回调函数,因此存在闭包。 That closure will now refer to the final value of i which is 4. 那个闭包现在将指的是i的最终值为4。

So in this case when the complete loop has executed the value of i is 4 but there are indexes upto 3 in x. 所以在这种情况下,当完成循环执行时, i值为4但x中的索引最多为3 That is why when you try to access x[4] you get undefined and you see TypeError to fix this just use let for fresh re-binding with the new value of i in every iteration: 这就是为什么当你尝试访问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);
}

Also if you cannot use let due to browser incompatibility you can do the trick with a IIFE: 此外,如果由于浏览器不兼容而无法使用let ,您可以使用IIFE执行此操作:

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

This works because var has function scope, so here in every iteration a new scope would be created along with the function with a new binding to the new value of i . 这是因为var具有函数作用域,因此在每次迭代中都会创建一个新的作用域以及具有对i的新值的新绑定的函数。

Because of missing i parameter - timeout probably used last (4) which was out of array. 由于缺少i参数 - 超时可能使用last(4),这是在数组之外。

But you can set&use function parameters by adding next timeout parameters. 但您可以通过添加下一个超时参数来设置和使用函数参数。

 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> 

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

This problem is related to a very basic and popular concept of Javascript called closure . 这个问题与一个非常基本和流行的Javascript概念有关,称为闭包 It can be solved in at least two ways: 它可以通过至少两种方式解决:

  1. Using let 使用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. Using IIFE 使用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);
}

You are trying to access x in a inner method, that is x is not defined in the scope of setTimeout that is why you receive that execption 您正试图在内部方法中访问x ,即x未在setTimeout范围内定义,这就是您接收该execption的原因

I would suggest you use a setInterval function as the solution Your code: 我建议你使用setInterval函数作为解决方案你的代码:

var randomNums = generateRandomNumbers(); 

for (var i = 0; i < 4; i++) { 

      setTimeout(function () { 
         x[i].innerHTML= ''; 
         x[i].innerHTML = randomNums[i]; 
     }, 3000 * i); 
}

A work around 一个解决方法

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

This issue appears to be caused by several factors. 这个问题似乎是由几个因素造成的。

  • Defining the generateRandomNumbers() function outside the setTimeout() scope. setTimeout()范围之外定义generateRandomNumbers()函数。
  • Using the var definition inside your for loop. 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);
}

This is a different implementation, but here's a Fiddle 这是一个不同的实现,但这是一个小提琴

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM