innerHTML = "" まとめ

HTML5をサポートしていないIE6〜IE8で、node.innerHTML = "

...
"; のように HTML5 で追加された新要素含んだ文字列を innerHTML に与えると、次のいずれかの条件が成立した場合に親子関係の崩れたサブツリーを生成してしまいます。

  1. innerHTML の前に document.createElement() が実行されていない
  2. node が DOM Tree に参加していない(オンザフライ)

これらを回避するには、IE6〜IE8で以下のようします。

document.createElement("section"); // HTML5 Shiv

function build(fragment) { // @param HTMLDocumentFragmentString: "<nav>...</nav>"
                           // @return DocumentFragment:
    var div = document.createElement("div");
    div.style.display = "none";
    document.body.appendChild(div); // DOM Tree に参加してから

    div.innerHTML = fragment; // innerHTML を実行する

    var rv = document.createDocumentFragment(); // DocumentFragment に移し替え
    while (div.firstChild) {
        rv.appendChild(div.firstChild);
    }
    document.body.removeChild(div); // DOM Tree から除去
    return rv; // DocumentFragment を返す
}

var documentFragment = build("<section>...</section>");

document.body.appendChild(documentFragment);

innerHTML の代わりに cloneNode() を使った場合は、生成される node の tagName と CSSセレクタでヒットする要素名にズレが発生しスタイルが適用されなくなります。

var div = document.createElement("div");
var section = document.createElement("section");
document.body.appendChild(div);
div.appendChild(section);
section.appendChild(document.createTextNode("..."));

var clonedNode = section.cloneNode(true);
alert( clonedNode.outerHTML ); // "<:section>...</:section>"

document.body.appendChild(clonedNode);

この時、clonedNode は document.getElementsByTagName("section") で検索できるが、 section { color: red } は適用されない状態になります。

CSSを適用するには以下のように、要素名の前に \: を追加したスタイルも設定します。

section,
\:section {
  color: red;
}

innershiv

jQuery ユーザの場合は、オンザフライで、$("div").append("section") のようにすると失敗してしまいます。
これを回避するには、HTML 5 innerShiv を使い、

$("div").append(innerShiv("<section>...</section>"));

のようにします。
innerShiv はDocumentFragmentを返す関数です。
内部では、DOM Tree に参加した状態で、innerHTML を行い、jQuery側で不具合が発生しないように回避します。