素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: twitter: @javascripter にどうぞ。

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にしても、速度の向上は見られない。