DocumentFragmentとdisplay:none、documentに直接追加する場合の速度比較
追記:コメント欄で指摘されたように、下のコードではイベントリスナは削除されなかった。
DocumentFragmentに一旦ストックすると、登録したイベントリスナは削除される。cloneNode、innerHTMLなども同様。
つまり、下のようなコードを書いて、ドキュメントをクリックしても、"click!"とは表示さない。
document.body.addEventListener("click", function(){ (console.log||alert)("click!"); },false); var df=document.createDocumentFragment(); df.appendChild(document.body); document.documentElement.appendChild(df);
不特定のサイトで動作するGreasemonkeyスクリプトで、ドキュメントの操作にDocumentFragmentを経由すると、サイト側のJavaScriptがうまく動作しなくなる可能性がある。
こういう場合は、直接ドキュメントをいじるか、変更したい最小限の要素をDocumentFragmentに追加したほうがいい。
例えば、リンクにwbr要素を追加するスクリプトの場合、
var df=document.createDocumentFragment(); df.appendChild(document.body); Array.slice(df.firstChild.getElementsByTagName("a")).forEach( function(a){ var t=a.textContent.split(/\b/); while(a.firstChild)a.removeChild(a.firstChild); for(var i=0,l=t.length-1;i<l;i++) { a.appendChild(document.createTextNode(t[i])); a.appendChild(document.createElement("wbr")); } a.appendChild(document.createTextNode(t[l])); }); document.documentElement.appendChild(df);
ではなく、
Array.slice(document.getElementsByTagName("a")).forEach( function(a){ var t=a.textContent.split(/\b/); var newa=a.cloneNode(true); while(newa.firstChild)newa.removeChild(newa.firstChild); for(var i=0,l=t.length-1;i<l;i++) { newa.appendChild(document.createTextNode(t[i])); newa.appendChild(document.createElement("wbr")); } newa.appendChild(document.createTextNode(t[l])); a.parentNode.replaceChild(newa,a); });
とすべき。この例だと、DocumentFragmentではなく、cloneNodeでドキュメントから切り離している。
ただし、a要素内の要素を考慮してないから、実際には使えない。
速度について。
document.body.innerHTML=""; console.time("pure"); var span=document.createElement("span"); span.textContent=location.href; for(var i=0;i<1000;i++){ var s=span.cloneNode(true); getComputedStyle(s,null); document.body.appendChild(s); } console.timeEnd("pure"); document.body.innerHTML=""; console.time("documentFragment"); var df=document.createDocumentFragment(); df.appendChild(document.body); var span=document.createElement("span"); span.textContent=location.href; for(var i=0;i<1000;i++){ var s=span.cloneNode(true); getComputedStyle(s,null); df.firstChild.appendChild(s); } document.documentElement.appendChild(df); console.timeEnd("documentFragment"); document.body.innerHTML=""; console.time("display:none"); document.body.display="none"; var span=document.createElement("span"); span.textContent=location.href; for(var i=0;i<1000;i++){ var s=span.cloneNode(true); getComputedStyle(s,null); document.body.appendChild(s); } document.body.display=""; console.timeEnd("display:none");
pure: 75ms
documentFragment: 47ms
display:none: 73ms
のような結果になった。
display:noneにしても、速度の向上は見られない。