JavaScript的DOM操作可以说是JavaScript最重要的功能,我们经常要根据用户的操作来动态的增加和删除元素,或是通过AJAX返回的数据动态生成元素。比如我们获得了一个很多元素的数组data[],需要将其每个值生成一个li元素插入到一个id为container的ul元素中,最简单(最慢)的方式是:var liNode, i, m;for (i = 0, m = data.length; i < m; i++) {liNode = document.createElement("li");liNode.innerText = data[i];document.getElementById("container").appendChild(liNode);}这里每一次循环都会去查找id为container的元素,效率自然非常低,所以我们需要将元素在循环前查询完毕,在循环中仅仅是引用就行了,修改代码为:var ulNode = document.getElementById("container");var liNode, i, m;for (i = 0, m = data.length; i < m; i++) {liNode = document.createElement("li");liNode.innerText = data[i];ulNode.appendChild(liNode);}缓存DOM对象的方式也经常被用在元素的查找中,查找元素应该是DOM操作中最频繁的操作了,其效率优化也是大头。在一般情况下,我们会根据需要,将一些频繁被查找的元素缓存起来,在查找它或查找它的子孙元素时,以它为起点进行查找,就能提高查找效率了。
在内存中操作元素
由于DOM操作会导致浏览器的回流,回流需要花费大量的时间进行样式计算和节点重绘与渲染,所以应当尽量减少回流次数。一种可靠的方法就是加入元素时不要修改页面上已经存在的元素,而是在内存中的节点进行大量的操作,最后再一并将修改运用到页面上。DOM操作本身提供一个创建内存节点片段的功能:document.createDocumentFragment(),我们可以将其运用于上述代码中:var ulNode = document.getElementById("container");var liNode, i, m;var fragment = document.createDocumentFragment();for (i = 0, m = data.length; i < m; i++) {liNode = document.createElement("li");liNode.innerText = data[i];fragment.appendChild(liNode);}ulNode.appendChild(fragment);这样就只会触发一次回流,效率会得到很大的提升。如果需要对一个元素进行复杂的操作(删减、添加子节点),那么我们应当先将元素从页面中移除,然后再对其进行操作,或者将其复制一个(cloneNode()),在内存中进行操作后再替换原来的节点
一次性DOM节点生成
在这里我们每次都需要生成节点(document.createElement("li")),然后将其加入到内存片段中,我们可以通过innerHTML属性来一次性生成节点,具体的思路就是使用字符串拼接的方式,先生成相应的HTML字符串,最后一次性写入到ul的innerHTML中。修改代码为:var ulNode = document.getElementById("container");var fragmentHtml = "", i, m;for (i = 0, m = data.length; i < m; i++) {fragmentHtml += "<li>" + data[i] + "</li>";}ulNode.innerHTML = fragmentHtml;这样效率也会有提升,不过手动拼写字符串是相当麻烦的一件事