可以在没有浏览器阻塞的情况下添加大量DOM节点吗?

时间:2022-11-25 18:03:27

I have a webpage on my site that displays a table, reloads the XML source data every 10 seconds (with an XmlHttpRequest), and then updates the table to show the user any additions or removals of the data. To do this, the JavaScript function first clears out all elements from the table and then adds a new row for each unit of data.

我的网站上有一个显示表的网页,每隔10秒重新加载XML源数据(使用XmlHttpRequest),然后更新表以向用户显示数据的任何添加或删除。为此,JavaScript函数首先清除表中的所有元素,然后为每个数据单元添加一个新行。

Recently, I battled thru a number of memory leaks in Internet Explorer caused by this DOM destroy-and-create code (most of them having to do with circular references between JavaScript objects and DOM objects, and the JavaScript library we are using quietly keeping a reference to every JS object created with new Element(...) until the page is unloaded).

最近,我通过这个DOM破坏和创建代码导致了Internet Explorer中的大量内存泄漏(大多数代码都与JavaScript对象和DOM对象之间的循环引用有关,而我们正在使用的JavaScript库静静地保持着引用用新元素(...)创建的每个JS对象,直到卸载页面为止。

With the memory problems solved, we've now uncovered a CPU-based problem: when the user has a large amount of data to view (100+ units of data, which equals 100 <tr> nodes to create, plus all of the table cells for each column), the process ties up the CPU until Internet Explorer prompts the user with:

随着内存问题的解决,我们现在发现了一个基于CPU的问题:当用户有大量数据要查看时(100多个数据单元,相当于要创建的100个节点,加上所有表每个列的单元格),该进程占用CPU,直到Internet Explorer提示用户:

Stop running this script?
A script on this page is causing Internet Explorer to run slowly. If it continues to run, your computer may become unresponsive.

停止运行此脚本?此页面上的脚本导致Internet Explorer运行缓慢。如果它继续运行,您的计算机可能会无响应。

It seems that running the row-and-cell-creation code times 100+ pieces of data is what is causing the CPU usage to spike, the function to take "too long" (from IE's perspective) to run, thus causing IE to generate this warning for the user. I've also noticed that while the "update screen" function runs for the 100 rows, IE does not re-render the table contents until the function completes (since the JS interpreter is using 100% CPU for that time period, I assume).

似乎运行行和单元创建代码超过100多个数据是导致CPU使用率飙升的原因,该函数需要“太长时间”(从IE的角度来看)才能运行,从而导致IE生成这个警告给用户。我还注意到,虽然“更新屏幕”功能运行100行,但IE不会重新呈现表内容,直到函数完成(因为JS解释器在该时间段内使用100%CPU,我假设) 。

So my question is: Is there any way in JavaScript to tell the browser to pause JS execution and re-render the DOM? If not, are there any strategies for handling creating large amounts of DOM nodes and not having the browser choke?

所以我的问题是:JavaScript中是否有任何方法告诉浏览器暂停JS执行并重新呈现DOM?如果没有,是否有任何处理创建大量DOM节点和没有浏览器阻塞的策略?

One method I can think of would be to handle the "update table" logic asynchronously; that is, once the Ajax method to reload the XML data is complete, put the data into some sort of array, and then set a function (using setInterval()) to run which will handle one element of the array at a time. However this seems a little bit like re-creating threading in a JavaScript environment, which seems like it could get very complicated (i.e. what if another Ajax data request fires while I'm still re-creating the table's DOM nodes?, etc.)

我能想到的一种方法是异步处理“更新表”逻辑;也就是说,一旦完成重新加载XML数据的Ajax方法,将数据放入某种数组,然后设置一个函数(使用setInterval())来运行,它将一次处理一个数组元素。然而,这似乎有点像在JavaScript环境中重新创建线程,看起来它可能会变得非常复杂(即,如果在我仍然重新创建表的DOM节点时,另一个Ajax数据请求会发生什么?等等)


update: Just wanted to explain why I'm accepting RoBurg's answer. In doing some testing, I've found that the new Element() method in my framework (I'm using mootools) is about 2x as slow as the traditional document.createElement() in IE7. I ran a test to create 1000 <spans> and add them to a <div>, using new Element() takes about 1800ms on IE7 (running on Virtual PC), the traditional method takes about 800ms.

更新:只是想解释为什么我接受RoBurg的答案。在进行一些测试时,我发现我框架中的新Element()方法(我使用的是mootools)大约是IE7中传统document.createElement()的2倍。我运行了一个测试来创建1000 并将它们添加到

,使用新的Element()在IE7上运行大约1800ms(在Virtual PC上运行),传统方法大约需要800ms。

My test also revealed an even quicker method, at least for a simple test such as mine: using DocumentFragments as described by John Resig. Running the same test on the same machine with IE7 took 247ms, a 9x improvement from my original method!

我的测试还揭示了一种更快的方法,至少对于像我这样的简单测试:使用John Resig描述的DocumentFragments。使用IE7在同一台机器上运行相同的测试需要247毫秒,比我原来的方法提高了9倍!

5 个解决方案

#1


100 <tr>'s isn't really that much... are you still using that framework's new Element()? That might be the cause of it.

100 并不是那么多......你还在使用那个框架的新元素()吗?这可能是它的原因。

You should test the speed of new Element() vs document.createElement() vs .innerHTML

您应该测试新Element()vs document.createElement()vs .innerHTML的速度

Also try building the dom tree "in memory" then append it to the document at the end.

还尝试在内存中构建dom树,然后将其附加到文档末尾。

Finally watch that you're not looking at .length too often, or other bits and bobs like that.

最后,请注意你不是经常看.length,或者其他比特和类似的东西。

#2


I have experienced similar problems at round 3000 table rows of complex data, so there is something not entirely right with your code. How is it running in firefox ? Can you check in several different browsers.

我在第3000轮表行的复杂数据中遇到过类似的问题,所以你的代码有些不完全正确。它是如何在Firefox中运行的?你能检查几种不同的浏览器吗?

Are you binding to onPropertyChange anywhere ? This is a really dangerous ie event that has caused me severe ie-specific headaches earlier. Are you using CSS selectors anywhere ? These are notoriously slow on ie.

你在任何地方都绑定到onPropertyChange吗?这是一个非常危险的事件,导致我早期严重的特定头痛。你在任何地方使用CSS选择器吗?这些都是出了名的慢。

#3


You could create a string representation and add it as innerHTML of a node.

您可以创建一个字符串表示并将其添加为节点的innerHTML。

#4


You could cloneNode(false) the element to be repeated, and then use that for the loop, instead of generating the element each time.

您可以cloneNode(false)要重复的元素,然后将其用于循环,而不是每次都生成元素。

#5


Avoid using one large for loop to render all of the data in one big step. Look at breaking up the data into smaller chunks instead.

避免使用一个大的for循环来渲染所有数据。看看将数据分解成更小的块。

Render each smaller chunk with a while loop. After that chunk is done call the next chunk with setTimeout [time 1ms] to give the browser a "breather".

使用while循环渲染每个较小的块。完成该块后,使用setTimeout [time 1ms]调用下一个块,为浏览器提供“喘息机会”。

You can avoid using the while loop all together and just use a setTimeout.

您可以避免同时使用while循环,只需使用setTimeout。

Using the setTimeout technique WILL slow down the execution speed a bit, but you should not get the warning message.

使用setTimeout技术会稍微降低执行速度,但是您不应该收到警告消息。

Another thing to look at not to append each element individually to the document. Look into creating a new tbody, appending the new rows to the new tbody, and append the tbody to the table.

另一件事是不要将每个元素单独附加到文档中。查看创建一个新的tbody,将新行追加到新的tbody,并将tbody附加到表中。

There are a lot of other things that can cause a slow application. It can be fun to flush them all out.

还有很多其他因素会导致应用程序变慢。将它们全部清除可能很有趣。

There is a neat study of the warning here:http://ajaxian.com/archives/what-causes-the-long-running-script-warning

这里有一个关于警告的简洁研究:http://ajaxian.com/archives/what-c​​auses-the-long-running-script-warning

#1


100 <tr>'s isn't really that much... are you still using that framework's new Element()? That might be the cause of it.

100 并不是那么多......你还在使用那个框架的新元素()吗?这可能是它的原因。

You should test the speed of new Element() vs document.createElement() vs .innerHTML

您应该测试新Element()vs document.createElement()vs .innerHTML的速度

Also try building the dom tree "in memory" then append it to the document at the end.

还尝试在内存中构建dom树,然后将其附加到文档末尾。

Finally watch that you're not looking at .length too often, or other bits and bobs like that.

最后,请注意你不是经常看.length,或者其他比特和类似的东西。

#2


I have experienced similar problems at round 3000 table rows of complex data, so there is something not entirely right with your code. How is it running in firefox ? Can you check in several different browsers.

我在第3000轮表行的复杂数据中遇到过类似的问题,所以你的代码有些不完全正确。它是如何在Firefox中运行的?你能检查几种不同的浏览器吗?

Are you binding to onPropertyChange anywhere ? This is a really dangerous ie event that has caused me severe ie-specific headaches earlier. Are you using CSS selectors anywhere ? These are notoriously slow on ie.

你在任何地方都绑定到onPropertyChange吗?这是一个非常危险的事件,导致我早期严重的特定头痛。你在任何地方使用CSS选择器吗?这些都是出了名的慢。

#3


You could create a string representation and add it as innerHTML of a node.

您可以创建一个字符串表示并将其添加为节点的innerHTML。

#4


You could cloneNode(false) the element to be repeated, and then use that for the loop, instead of generating the element each time.

您可以cloneNode(false)要重复的元素,然后将其用于循环,而不是每次都生成元素。

#5


Avoid using one large for loop to render all of the data in one big step. Look at breaking up the data into smaller chunks instead.

避免使用一个大的for循环来渲染所有数据。看看将数据分解成更小的块。

Render each smaller chunk with a while loop. After that chunk is done call the next chunk with setTimeout [time 1ms] to give the browser a "breather".

使用while循环渲染每个较小的块。完成该块后,使用setTimeout [time 1ms]调用下一个块,为浏览器提供“喘息机会”。

You can avoid using the while loop all together and just use a setTimeout.

您可以避免同时使用while循环,只需使用setTimeout。

Using the setTimeout technique WILL slow down the execution speed a bit, but you should not get the warning message.

使用setTimeout技术会稍微降低执行速度,但是您不应该收到警告消息。

Another thing to look at not to append each element individually to the document. Look into creating a new tbody, appending the new rows to the new tbody, and append the tbody to the table.

另一件事是不要将每个元素单独附加到文档中。查看创建一个新的tbody,将新行追加到新的tbody,并将tbody附加到表中。

There are a lot of other things that can cause a slow application. It can be fun to flush them all out.

还有很多其他因素会导致应用程序变慢。将它们全部清除可能很有趣。

There is a neat study of the warning here:http://ajaxian.com/archives/what-causes-the-long-running-script-warning

这里有一个关于警告的简洁研究:http://ajaxian.com/archives/what-c​​auses-the-long-running-script-warning