innerHTML や jQuery.html() は HTMLをそのまま取得できるわけではない
element.innerHTML
を使うと、その要素内の HTML を取得することができます。Mozilla Developer Network (MDN) には次のように書いてあります。
innerHTML は、与えられた要素に含まれる全てのマークアップ と内容を設定または取得します。
また、jQuery.html() も、innerHTML と同様、HTML を取得することができます。
Get the HTML contents of the first element in the set of matched elements.
-- .html() – jQuery API より
でも、これらのメソッドを使っても、HTML のソースをそのまま取得できるわけではないことをご存知でしたか?私は知らずにハマってしまいました。ということで、以下、調べてみた結果です。
例 1
<div id="main">
<ul>
<li>coffee</li>
<li>coke</li>
<li>red bull</li>
</ul>
</div>
<script>
var html = document.getElementById('main').innerHTML;
console.log(html);
</script>
この例では、次のような期待通りの HTML が返ってきます。
<ul>
<li>coffee</li>
<li>coke</li>
<li>red bull</li>
</ul>
例 2
しかし、次の場合はどうでしょうか?
<div id="main">
<p><div>foo</div></p>
</div>
<script>
var html = document.getElementById('main').innerHTML;
console.log(html);
</script>
コンソールに出力されるのは
<p><div>foo</div></p>
だと期待するかもしれませんが、実際は
<p></p>
<div>foo</div>
<p></p>
という文字列が返ってきます。jQuery.html() の場合も同じ結果になります。
なぜこのようなことが起こるのでしょうか?不思議に思い、Quora で質問を投げてみました。すると、すぐにどなたかが答えてくれました。(Quora すげー!)また、Twitter でも同様の質問を投げてみたところ、@hogenishi1122 さんが答えてくれました。(感謝!)
innerHTML や jQuery.html() は HTML のソースをそのまま返すわけではなく、ブラウザが一度 DOM ツリーに変換したものを返すとのこと。従って、invalid な HTML コードだと、ブラウザが解釈した DOM ツリーが返ってくるというわけでした。
invalid な HTML を書かなければ、さほど問題にはならないかもしれませんが、必ずしも世の中がすべて valid だとは限りません。記憶の片隅に留めておけば、無用な苦労をすることもないかと思います。