DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。
不过它有一种特殊的行为,该行为使得它非常有用,即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作
同时我们了解到当需要对页面DOM进行操作的时候必然会导致多次 重绘、回流(什么是重绘,回流?)。这样会加大页面的负担。影响页面性能。因此我们可以创建这样一个临时占位符。
测试 demo:
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <div id="box"> <button onclick="addList()">Button</button> <button onclick="addList2()">Button</button> </div> <div> <ul id="myList"> <li>item</li> </ul> <hr> </div> <script type="text/javascript"> function addList(){ var doc = document, list = doc.getElementById('myList'), item, i; console.time('addList'); for(i=0; i<1000; i++){ item = doc.createElement('li'); list.appendChild(item); item.appendChild(doc.createTextNode('item '+ i)); console.log(list.clientHeight); } console.timeEnd('addList'); } function addList2(){ var doc = document, list = doc.getElementById('myList'), fragment = doc.createDocumentFragment(), item, i; console.time('addList2'); for(i=0; i<1000; i++){ item = doc.createElement('li'); item.appendChild(doc.createTextNode('item '+ i)); fragment.appendChild(item); console.log(list.clientHeight); } list.appendChild(fragment); console.timeEnd('addList2'); } </script> </body> </html>
借用 在知乎上 网友 付星昱 的评论 解释一下:
直接往元素里 append的操作会引发 reflow,在循环中多次触发 reflow是非常不讨好的事情,
我们聪明的现代浏览器会将短时间内的多次 reflow收集起来组成队列,在一定时间后 flush队列,将多个 reflow的变为一次 reflow。
直接获取 offsetHeight会导致浏览器马上 flush队列,计算出当前值。
除了计算 offsetHeight,clientHeight/clientWidth,offsetWidth,scrollTop/Left/Height/width也会使浏览器立即 flush队列。
可以理解为什么 我之前多次测试 而看到的 两种方式 在时间上没什么差异的原因了;