无法在 setTimeout 内设置 innerHTML
Posted
技术标签:
【中文标题】无法在 setTimeout 内设置 innerHTML【英文标题】:Cannot set innerHTML inside setTimeout 【发布时间】:2020-01-03 01:18:50 【问题描述】:尝试设置一个 html 类的 innerHTML,它们是四个框,每个框依次设置 3 秒。当 innerHTML 设置为加载图标时,我可以在没有 setTimeout 的情况下设置 innerHTML。当将 innerHTML 放入 setTimeout 时,将返回以下内容:'Uncaught TypeError: Cannot set property 'innerHTML' of undefined'。
尝试调试我的代码,将消息发送到控制台并搜索 ***,但没有成功。
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
【参考方案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。至少可以通过两种方式解决:
-
使用
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);
-
使用 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
【讨论】:
以上是关于无法在 setTimeout 内设置 innerHTML的主要内容,如果未能解决你的问题,请参考以下文章
使用 Node 进行 Jest 测试 - 超时 - 在 jest.setTimeout 指定的 5000 毫秒超时内未调用异步回调
超时 - 在 jest.setTimeout 指定的 5000 毫秒超时内未调用异步回调