Javascript 更新按钮点击代码开头的样式;执行后删除

Javascript 更新按钮点击代码开头的样式;执行后删除,javascript,css,Javascript,Css,我有一段代码,单击按钮时执行。代码使用的循环有时需要一段时间才能完成。当用户单击按钮时,我希望光标在循环开始之前更改“等待”光标。循环完成后,光标应恢复正常 实际发生的情况(至少在Chrome for Windows中)是,样式在循环之后才会更新。这似乎是按钮如何工作的一个怪癖。我真的不知道。我猜不到了 一个例子:(它只是使用console.log来执行“某事”…您可能需要更改循环运行的次数,具体取决于您的机器的速度) 示例HTML: <div class="fakebody">

我有一段代码,单击按钮时执行。代码使用的循环有时需要一段时间才能完成。当用户单击按钮时,我希望光标在循环开始之前更改“等待”光标。循环完成后,光标应恢复正常

实际发生的情况(至少在Chrome for Windows中)是,样式在循环之后才会更新。这似乎是按钮如何工作的一个怪癖。我真的不知道。我猜不到了

一个例子:(它只是使用console.log来执行“某事”…您可能需要更改循环运行的次数,具体取决于您的机器的速度)

示例HTML:

<div class="fakebody">
    <button id="foo">Foo</button>
</div>
JavaScript示例:

$('#foo').on('click', function (e) {
    $('.fakebody').addClass('wait');
    for (i = 0; i < 10000; i++) {
        console.log(i);
    }
    $('.fakebody').removeClass('wait');
});
$('foo')。在('click',函数(e)上{
$('.fakebody').addClass('wait');
对于(i=0;i<10000;i++){
控制台日志(i);
}
$('.fakebody').removeClass('wait');
});
--

以下是我对脚本应该如何工作的假设:

  • 单击发生,触发代码。事实上,如果我在代码块中记录“started!”,它将正确地记录它已经启动
  • 只要光标悬停在“fakebody”上的任何位置,它就应该是等待光标
  • for循环只是一种简单的方法,可以消磨几秒钟来查看效果。您可以随意替换任何其他需要一段时间才能完成的循环
  • 循环结束时,光标不再是等待光标
实际发生的情况:

  • 循环执行
  • 循环结束时,光标变为“等待”光标,然后立即变回常规光标。直到循环完成,更改才会发生
有人知道在循环开始之前而不是在循环完成之后改变光标的技术或解决方法吗?这是我需要自我教育的已知行为吗(如果是,你知道我应该从哪里开始寻找吗?

-使用&来构建在jQuery中应该发生什么的顺序

$('#foo').on('click', function (e) {

    $('.fakebody').addClass('wait').queue(function(n) {  

        for (i = 0; i < 10000; i++) { console.log(i); }

    }).removeClass('wait').dequeue();

});    
$('foo')。在('click',函数(e)上{
$('.fakebody').addClass('wait').queue(函数(n){
对于(i=0;i<10000;i++){console.log(i);}
}).removeClass('wait').dequeue();
});    

我认为您应该尝试通过隐藏+显示父元素来强制重画。 试试这个:

document.getElementById('fakebody').style.display = 'none';
document.getElementById('fakebody').style.display = 'block';
循环之前和之后(即,当您希望子元素“foo”刷新时)

编辑:因为您使用的是jquery,所以可以执行以下操作:

$('#fakebody').hide().show(0);

这是JavaScript中的一个常见问题。可能会提供一些更深入的见解,但本质上是,同步JavaScript执行必须在浏览器执行其他操作之前完成(如更新视图)

由于
.addClass
for
循环和
.removeClass
都是同步发生的,浏览器没有机会重新绘制任何内容。在这些情况下,经常使用的一种技术是
设置超时
,超时时间为
0
,这实际上只是将控制权“让给”浏览器

$('.fakebody').addClass('wait');

setTimeout(function() {
    for (i = 0; i < 10000; i++) {
        console.log(i);
    }
    $('.fakebody').removeClass('wait');
}, 0);
并像这样使用它:

yieldLongRunning(function() {
    $('.fakebody').addClass('wait');
},
function() {
    for (i = 0; i < 10000; i++) {
        console.log(i);
    }
},
function() {
    $('.fakebody').removeClass('wait');
});
yieldLongRunning(函数(){
$('.fakebody').addClass('wait');
},
函数(){
对于(i=0;i<10000;i++){
控制台日志(i);
}
},
函数(){
$('.fakebody').removeClass('wait');
});

作为补充,请注意,
setTimeout(…,0)
只是将函数与其他排队的JavaScript函数以及其他类型的事件(如重画)一起排队,在浏览器的事件循环中。因此,没有任何
setTimeout
调用保证在给定时间精确运行-timeout参数只是一个下限(事实上,有一种浏览器用来防止无限超时循环的方法;不过,您仍然可以使用
0
,浏览器会在最小延迟后将其添加到事件队列中).

Brian,我非常感谢您在这个答案中所做的工作,最终我更喜欢语义,而不是setTimeout方法。但是我的产品代码比我的示例要复杂一点,我实际上无法链接队列和出列事件。因为在产品中我使用setTimeout方法,所以我接受了answe的说法r、 可读性有时是一种奢侈品=)不用担心,不管使用情况如何,我都很乐意提供。另外,我学习了@voithos的方法,非常有趣,这让我陷入了一个信息的兔子洞。干杯。好书->它有效,我“了解”它是如何产生控制的。语义是“不确定的”,我希望未来的开发人员明白为什么需要它(可能会在那里发表评论)但不可否认,它确实起到了作用!@GregPettit:事实上,JavaScript的一些异步方面在您了解一些内部工作原理之前并不完全直观。我在答案中添加了更多内容,讨论了您可能希望如何使此模式更易于维护。太好了!感谢您的回答以及增强的pat我可以进一步扩展它,允许函数参数接受单个函数或函数数组。
/**
 * Wraps a long-running JavaScript process in a setTimeout
 * which yields to allow the browser to process events, e.g. redraw
 */
function yieldLongRunning(preFn, fn, postFn, ctx) {
    if (arguments.length <= 2) {
        ctx = fn; fn = preFn;
        preFn = postFn = function() {};
    }
    preFn.call(ctx);
    setTimeout(function() {
        fn.call(ctx);
        postFn.call(ctx);
    }, 0);
}
yieldLongRunning(function() {
    $('.fakebody').addClass('wait');
},
function() {
    for (i = 0; i < 10000; i++) {
        console.log(i);
    }
},
function() {
    $('.fakebody').removeClass('wait');
});