嗯,我不知道究竟什么是一个好头衔,因为这是一个最特殊的情况,或者我是异常愚蠢的。
这就是我想要做的事情。
创建一个简单的<meter>
标记,这是HTML5中的新标记。主要问题是我的javascript。我试图在我的javascript中逐渐增加米标签的值。但不知怎的,它不按我想要的方式工作。
的JavaScript。
for (var i = 0; i <= 10; i++) {
var a = document.getElementById("mtr1");
setTimeout(function () {
console.log(i);
a.value = i;
}, 250);
}
我试图每250毫秒逐渐增加仪表的值。这不会发生。相反,仪表直接跳到10。
我感兴趣的是我在控制台中获得的i
的价值。我得到10
的实例,而不是1,2,3 ... 10。
为什么会这样?
答案 0 :(得分:7)
这是一个JavaScript闭包的经典之作。这里i
是对变量的实际引用,而不是它的副本。在遍历循环后,它的值为10,这就是为什么所有日志调用都将10写入日志。
这应该更好:
for (var i = 0; i <= 10; i++) {
var a = document.getElementById("mtr1");
setTimeout(function (i) {
return function() {
console.log(i);
a.value = i;
};
}(i), 250 * i);
}
这里最内部的i
是setTimeout
的回调参数,而不是你在循环体中声明的变量。
答案 1 :(得分:7)
您应该阅读有关JavaScript中的闭包的更多信息。当变量被关闭时,它是完全相同的变量,而不是副本。由于setTimeout
是异步的,整个循环在任何函数运行之前结束,因此i
变量在任何地方都是10
。
function incMtrAsync(max, delay, el) {
if (el.value++ < max) {
setTimeout(incMtrAsync.bind(null, max, delay, el), delay);
}
}
incMtrAsync(10, 250, document.getElementById("mtr1"));
上述实现使用递归方法实现循环。每次调用inMtrAsync
时,它都会检查仪表的value
是否达到最大值,如果没有,则通过回调注册另一个超时。
如果你想延迟初始增量,只需将第一个调用包装在另一个超时中。
setTimeout(incMtrAsync.bind(null, 10, 250, document.getElementById("mtr1")), 250);
答案 2 :(得分:5)
没有人使用过setInterval,所以这是我的解决方案(http://jsfiddle.net/Qh6gb/4/):
var a = document.getElementById("mtr1");
var i = 0;
var interval = setInterval(function () {
console.log(i);
a.value = ++i;
if (i == 10) {
clearInterval(interval);
}
}, 250);
答案 3 :(得分:3)
您描述的问题发生在原始版本中对setTimeout
的异步调用看到10
的{{1}}值之前,因为这是执行回调时的值。
所以,这是关闭范围的一个问题,为了使它工作你应该像这样:
i
另外,由于for (var i = 0; i <= 10; i++) {
var a = document.getElementById("mtr1");
(function (i, a) {
setTimeout(function () {
console.log(i);
a.value = i;
}, 250);
})(i, a);
}
始终相同,因此应该更好:
a
如果那时你想看到计数器“勾选”,这将使其可见:
var a = document.getElementById("mtr1");
for (var i = 0; i <= 10; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
a.value = i;
}, 250);
})(i);
}
答案 4 :(得分:2)
这是因为您调用了setTimeout,这是“异步”。因此setTimeout被调用10次,但是在完成整个循环之后它被执行。因此,每次通话i = 10 ......
有解决方案:
var i = 1,
meter = document.getElementById("mtr1");
function increase() {
meter.value = i++;
console.log(i);
if(i<=10) {
setTimeout(increase, 250);
}
}
setTimeout(increase, 250);
答案 5 :(得分:1)
你可以使用timeout
jquery插件: 更容易
但是你应该计算你的超时,
对你而言,timeout=250*max=250*10=2500
所以
$('meter').timeout(2500);
答案 6 :(得分:0)
在函数内部运行for循环,而不是在循环的每一步中声明一个闭包。
JSFIDDLE: http://jsfiddle.net/Qh6gb/3/
var a = document.getElementById("mtr1");
setTimeout(function () {
for (var i = 0; i < 10; i++) {
console.log(i);
a.value = i;
}
}, 250);
答案 7 :(得分:0)
我希望我理解正确。如果你有解决方案,请试着告诉我。
var count = 0;
function increment(){
document.getElementById("meter").value = count;
count++;
if(count ==10)
count=0;
}
setInterval(increment, 250);
请查看jsFiddle
答案 8 :(得分:-1)
您正在创建多个同时被启动的功能。 将计时器乘以i以获得正确的延迟。
for (var i = 0; i <= 10; i++) {
var a = document.getElementById("mtr1");
setTimeout(function () {
console.log(i);
a.value = i;
}, 250 * i);
}