如果我在Javascript事件处理程序中对DOM进行更改,这些更改是增量呈现,还是只在最后呈现一次

If I make DOM changes in a Javascript event handler, are the changes rendered incrementally, or just once at the end?

本文关键字:一次 最后 程序 事件处理 Javascript 如果 DOM      更新时间:2023-09-26

假设我有以下javascript事件处理程序:

function handleEvent(e){
    document.body.style.backgroundColor = 'green';
    longRunningFunction();
    document.body.style.backgroundColor = 'red';
}

浏览器会先显示绿色背景,然后切换为红色吗?还是直接显示红色背景?

根据我的测试,它直接在事件处理程序的末尾显示红色。但是,这是规范的一部分,还是只是浏览器实现方式的附带因素?

更新:

我应该澄清,我不是"瞄准"这种效果。更确切地说,我想保证它不会发生而不会发生。我的一些事件处理程序改变了很多东西,如果我可以假设没有任何中间状态被呈现,这将使我的工作更轻松。

下面是JavaScript执行环境中会发生的事情:

  1. document.body.style.backgroundColor将其值更改为'green'
  2. 一个长时间运行的进程将会运行。
  3. document.body.style.backgroundColor将其值变为'red'

如果JavaScript线程被阻塞,backgroundColor属性的变化将如何影响页面的外观是未指定的。

这里有一个例子。如果你点击hello,你会看到一个轻微的延迟,然后背景会变成红色:

function handleEvent(e){
    document.body.style.backgroundColor = 'green';
    longRunningFunction();
    document.body.style.backgroundColor = 'red';
}
function longRunningFunction() {
    for(var i=0; i<2000000000; ++i) { +i; }
}
document.body.addEventListener("click", handleEvent);
<div>hello</div>

这是因为浏览器的渲染引擎在响应CSS属性的改变时被长时间运行的阻塞JavaScript函数阻塞了。这不是指定的行为,而是所有浏览器的实现现实。请参阅如何在脚本执行一些繁重的处理时强制浏览器重新绘制?对于类似的问题。

我认为任何给定的浏览器都可以选择在JavaScript线程并发运行一个单独的重绘线程,但我不相信目前任何主流浏览器都这样做。在这种方法中可能存在固有的竞争条件复杂性;我从来没有写过浏览器,所以我不知道。


如果你想要显示中间状态,你可以在将backgroundColor设置为绿色后使用一个非常快速的setTiemout调用,以清除JavaScript函数堆栈并允许渲染器重新绘制页面:

function handleEvent(e){
    document.body.style.backgroundColor = 'green';
    setTimeout(function() {
        longRunningFunction();
        document.body.style.backgroundColor = 'red';
    }, 4);
}

这将longRunningFunction和第二次颜色更改排队,以便将来在4毫秒内运行。在这空闲的4毫秒内,渲染器有机会重新绘制页面。

这是否说明了您的目标?

function handleEvent(e){
    document.body.style.backgroundColor = 'green';
    alert();
    document.body.style.backgroundColor = 'red';
}