progd's blog

おいしく楽しくすこやか節酒

jQuery の .html(htmlString) を呼ぶ際、コメント部にタグの文字列が入っていると、余計なタグが付与されてしまう場合がある

以下のコードで再現する。 result1result2 は、いずれも tr タグの配下に td タグを追加するものだが、 result2 のほうは tr が余計に付いている。

var result1 = $('<tr></tr>').html('<td>hoge</td>');
console.log(result1.html());
// => <td>hoge</td>

var result2 = $('<tr></tr>').html('<!--<tr>--><td>hoge</td>');
console.log(result2.html());
// => <!--<tr>--><tr><td>hoge</td></tr>

html() は innerHTML 相当なので、 result2 は tr が二重になってしまう。

例えば、上記の結果を table タグに append したりすると、以下のようになる。

console.log($('<table></table>').append(result1).html());
// => <tbody><tr><td>hoge</td></tr></tbody>
                                                                
console.log($('<table></table>').append(result2).html());
// => <tbody><tr><!--<tr>--><tr><td>hoge</td></tr></tr></tbody>
//           ^^^^           ^^^^
//           tr タグが二重になっている

結論は「コメントに変なものを書かないようにしましょう」でいいのかな…*1

ところで、以下の例ではこの問題は発生しない。 親要素と同じタグが、コメントに含まれている場合だけ発生するのだろうか…

var result3 = $('<tr></tr>').html('<!--<table>--><td>hoge</td>');
console.log(result3.html());
// => <!--<table>--><td>hoge</td>

参考: .html() | jQuery API Documentation

追記: バージョン書くの忘れてた

以下蛇足

この問題は、 JavaScript エンジニア養成読本という書籍の Backbone.js 入門を写経しているときに遭遇したもの。

第 6 章で、ノートアプリの一覧表示を動作確認するのだが、図 4 に比べて、テーブルがおかしい。 tbody の行が左寄りになっている。

f:id:progd:20150508103216p:plain

(確認用に、罫線が見えるよう table-bordered クラスを指定している)

  • インスペクタで見ると、 tr タグが二重になっているようだ…
  • デバッガで、 note_list_item.js の render メソッドブレークポイントを設定し、 return 直前のときの各変数を調べてみる。
    • ローカル変数 html の中身は、まだ tr タグが付いていないようだ。
    • しかし、 this のプロパティを調べていくと、 el.outerHTML というプロパティがあり、これは tr タグが二重になってしまっている (下記画像の下部、 Console の内容からわかる) 。
    • this.$el.html(html) の呼び出し時に、 tr タグが 1 個だけ付くべきなのに、 2 個になってしまっているのか?
  • this.$el.html(html) ではテンプレート HTML を渡している。
    • テンプレートからコメントを除いたら直った!
    • 幾つかのパターンを試した結果、冒頭に記載した通り、 jQuery の仕様らしいことがわかった。
    • テンプレートを写経する際、律儀にコメントの内容まで写していたので…
      • 書籍発売後に jQuery の仕様が変わったのだろうか?

最初に使用していたテンプレート HTML

<script type="text/template" id="noteListItemView-template">
  <!--
    個々のメモ情報を表示する<tr>要素のためのテンプレート
    <tr>要素自体はBackbone.Viewが生成する
  -->
  <td>
    <a href="#">
... (以下略)

デバッガの実行結果

*1:仕様なのかバグなのかもわかってない