我们都知道,DOM操作的效率是很低的,尤其是用JS操作DOM的时候,性能的优劣更是引发问题和争论的焦点。
这里我们先分析一个很简单的例子:
<ul>
<li>1111</li>
<li>1111</li>
<li>1111</li>
<li>1111</li>
<li>1111</li>
<li>1111</li>
...
</ul>
假设我们要对上面的1000个或者更多的 li 元素进行抽样显示(隐藏)的控制,照常理来讲,我们会习惯性地去遍历这些元素,加上相应class,或者直接写上内联样式。至少在我看来这是最简单最高效的操作。然而 试验 结果却出乎我的意料,IE和firefox的显示结果都出奇的慢,去网上找了答案:这其实和文档的回流(reflow)很有关系,Nicholas总结了引起reflow的一些因素,其结论就是:
- 当对DOM节点执行新增或者删除操作时。
- 动态设置一个样式时(比如element.style.width=”10px”)。
- 当获取一个必须经过计算的尺寸值时,比如访问offsetWidth、clientHeight或者其他需要经过计算的CSS值(在兼容DOM的浏览器中,可以通过getComputedStyle函数获取;在IE中,可以通过currentStyle属性获取)。
文章地址是:www.nczonline.net/blog/2009/02/03/speed-up-your-javascript-part-4/
怿飞师父在他的 blog 里也有提到更详细的因素,有兴趣的可以再去深入研究。
当然我写这篇文章的目的不是去翻译 Nicholas 的文章或是照搬他们的理论,而是想分享下我 测试 的心得:
测试地址(慎用,会卡哦~~~^_^):http://www.silentash.com/upload/domscript.html
1. 现有主流浏览器对DOM的操作是各自为政的,IE&Chrome reflow过程很短或者说没有reflow,Firefox的reflow现象非常显著(脚本执行之后有很长时间去刷新DOM结构,而且是硬直时间),Opera次之。
2. reflow过程中浏览器无法响应用户的操作。
3. IE耗费的大部分时间是去获取DOM元素,比如在这个例子中我使用的是常规的getElementById方法,遍历获取元素的时候很耗费时间。而其他浏览器多半时间花在后续的DOM操作中。
4. 只添加class不做样式处理不会触发reflow,只更改背景样式也不会触发reflow。
5. Nicholas文中提到循环中的语句是会实时触发reflow的,但实际测试的结果是 reflow在执行环境结束之后才被触发, Dean Edwards 在其回复中也证实了这一点。
6. 同数量的DOM操作,appendChild方法比更改样式的方法高效很多,也就是说display:none引起的reflow消耗比DOM节点的新增或删除要严重(firefox中尤其明显)
综上几点,再回顾刚才的例子,更高效的方法是用appendChild方法去增加需显示的DOM节点,于是便有了最后的 优化措施:
1. 预存储UL下的所有LI元素
2. 获取需显示的LI列表
3. 删除整个UL元素,这里用的是UL父元素.removeChild(UL),千万不要用innerHTML=”,火狐里会挂的很惨。
4. 重建UL元素
5. 逐个添加步骤2获得的LI元素到UL
6. 添加UL到UL父元素。
这其中又有Fragment的问题,究竟什么时候该用Fragment?因为fragment仅仅是一个过渡容器,在本质上不能提升代码的效率。所以很简单,当你需要临时容器的时候就用fragment,当你不需要临时容器的时候就可以不用。上面的过程中UL的重建等于是一个fragment,直到所有的LI都添加进去,才append到文档中。上面的例子中,如果用ul.innerHTML = ”,则可以创建一个fragment作为替身……
另:例子 中用到的是原生的DOM操作方法,如果用集成库的话性能上还可再优化一些。




如果给出执行时间的对比,更有说服性
appendChild和innerHTML的效率谁更高呢?这个问题一直很困扰。这里请教一下。
我断章取义的理解一下文中的某个意思,是说如果想让一个元素不显示。与其用display:none,不如把它放在一个fragment中存着,然后把本体删了,当需要用的时候再把fragment放回原来的位置?
我会用个更WS的办法去做成,就是先把ul的innerHTML存到一个变量中,然后把所有的li都当成字符串来处理,处理完了以后再写回innerHTML,这样只操作了两次dom,其余的都是字符串的操作~~我先测试一下,虽然我不敢保证效率会很高,但是~~操作dom都是以牺牲性能为前提,就自己先评估一下吧~~
肯定的嘛。。。第一取的是原始的确良innerHTML,全部处理完了,再一次写入innterHTML~~~不过中间的操作,要么就是用数组,要么就是用正则去做了,呵呵~~~
你说一些js库的innerhtml操作是不是进行了优化了的?
比如说jquery
我现在正好有这么个问题,innerHTML=str,这个str内容很大,其实就是1000多条td数据组成,每个td里的内容又是硬编码加了很多onclick元素的。
我最初的做法,是循环遍历,每次添加单个append
后来一次性把td组合好,一次性添加,但还是很慢。速度没和之前的比较过
后来我在server段组合好这个str,再添加到这个dom对象里,效率提高了差不多一倍,但是添加这个动作还是花了30s
请问有什么好的处理办法吗?
因为客户只用IE,所以不考虑FF
看了你的文章收获很大,我现在遇到的问题,在执行页面元素的移动动画的过程中,不论是使用setInterval 定时移动 还是用jquery 的animate ,都会大量的改变样式,产生大量的reflow操作,可能是这样使得动画显得很卡,请问下有什么好办法解决.
您好,您的博客很棒,我是前端热爱者,不过对您的“…IE的innerHTML效率优于appendChild,而Firefox则是相反….”观点有点怀疑,我做过测试,不知道您怎么认为的,这是我的博客:http://hi.baidu.com/hulk168,您有兴趣的话,可以踩踩,谢谢
顶 clone 兄,看你的文章了,app由于inner