我希望自己不会愚弄自己,但我试图了解这两行代码中发生的事情:
document.body.innerHTML = 'something';
alert('something else');
我观察到的是在HTML更新之前警报显示(或者可能已经更新,但页面没有被刷新/重新绘制/无论如何)
查看此codepen以了解我的意思。
请注意,即使将alert
放入setTimeout(..., 0)
也无济于事。看起来innerHTML
实际更新页面需要更多的事件循环。
编辑:
我忘了提及我使用Chrome并没有检查其他浏览器。看起来它只在Chrome中可见。尽管如此,我仍然感兴趣为什么会发生这种情况。
答案 0 :(得分:129)
设置innerHTML是同步的,您可以对DOM进行大多数更改。但是,呈现网页是另一回事。
(请记住,DOM代表"文档对象模型"它只是一个"模型",数据的表示。用户在屏幕上看到的是该模型应该如何看的图片。因此,更改模型并不会立即改变图片 - 需要一些时间来更新。)
运行JavaScript并呈现网页实际上是分开进行的。简单地说,首先运行页面上的所有JavaScript(从事件循环 - 检查this excellent video以获取更多详细信息)然后在之后浏览器呈现对网页的任何更改供用户查看。这就是为什么"阻止"这是一个大问题 - 运行计算密集型代码阻止浏览器超越运行JS"步骤进入"渲染页面"步骤,导致页面冻结或口吃。
Chrome的管道如下所示:
正如您所看到的,所有JavaScript都是先发生的。然后页面被样式化,布局,绘制和合成 - "渲染"。并非所有此管道都将执行每一帧。这取决于更改的页面元素(如果有)以及如何重新呈现它们。
注意:alert()
也是同步的,并且在JavaScript步骤中执行,这就是在您看到网页更改之前出现警告对话框的原因。
你现在可以问"坚持,在那个' JavaScript'进入管道?我的所有代码每秒运行60次吗?"答案是" no",它可以追溯到JS事件循环的工作原理。 JS代码只有在堆栈中才会运行 - 从事件监听器,超时等等。见previous video(真的)。
https://developers.google.com/web/fundamentals/performance/rendering/
答案 1 :(得分:25)
是的,它是同步的,因为它可以工作(继续,在你的控制台中输入它):
document.body.innerHTML = 'text';
alert(document.body.innerHTML);// you will see a 'text' alert
在您看到页面更改之前看到警报的原因是浏览器渲染需要更多时间,并且不能逐行执行javascript。
答案 2 :(得分:6)
innerHTML
属性实际上会同步更新,但此更改导致的可视重绘异步发生。
视觉呈现DOM在Chrome中是异步的,直到当前的JavaScript函数堆栈清除并且浏览器可以自由接受新事件之后才会发生。其他浏览器可能使用单独的线程来处理JavaScript代码和浏览器呈现,或者当警报停止执行另一个事件时,它们可能会让某些事件获得优先级。
您可以通过两种方式看到这一点:
如果您在提醒之前添加for(var i=0; i<1000000; i++) { }
,则表示您已经给浏览器足够的时间进行重绘,但它没有,因为功能堆栈尚未清除( add
仍在运行。
如果您通过异步alert
推迟setTimeout(function() { alert('random'); }, 1)
,则重绘过程将超过setTimeout延迟的函数。
0
超时,则无效,可能是因为Chrome在任何其他事件之前(或至少在重绘事件之前)将事件队列优先级提供给0
超时。 / LI>