innerHTMLとjQuery.text()の間のDOMNodeInsertedとDOMSubtreeModifiedの挙動の違い

Chrome拡張作ってて、ある要素のテキストの変更を監視しようとしたときに気づいたのでメモ。
書いてあること以上のことは調べていない。*1

要素内のテキストをjQuery.text()で変更している場合と、innerHTMLで変更している場合とで発生するDOMNodeInsertedとDOMSubtreeModifiedに差異があってなんだかなーなんだかなーという話。

とあるh1要素の変更を監視するため、以下のように DOMNodeInsertedとDOMSubtreeModified を設定。

document.querySelector('div.foo h1').addEventListener('DOMNodeInserted', function(e){
    console.log('***DOMNodeInserted:' + e.srcElement.constructor.name);
});
document.querySelector('div.foo h1').addEventListener('DOMSubtreeModified', function(e){
    console.log('***DOMSubtreeModifed:' + e.srcElement.constructor.name);
});

Chromeのコンソールから、jQueryのtext() でh1要素の内のテキストを変更した場合。

> $('div.foo h1').text('hoge')
***DOMSubtreeModifed:HTMLHeadingElement
***DOMNodeInserted:Text
***DOMSubtreeModifed:HTMLHeadingElement

innerHTMLでh1要素の内のテキストを変更した場合。

> document.querySelector('div.foo h1').innerHTML = "fuga"
***DOMSubtreeModifed:Text

jQuery.text()では、どうやら innerHTMLを空文字にしてから、新しい文字列を設定しているらしい。*2

> document.querySelector('div.foo h1').innerHTML = ""
***DOMSubtreeModifed:HTMLHeadingElement
> document.querySelector('div.foo h1').innerHTML = "fuga"
***DOMNodeInserted:Text
***DOMSubtreeModifed:HTMLHeadingElement

なので、一回目のDOMSubtreeModifiedを拾ってもh1に設定されているのは空文字となる。

まとめると、

  • jQueryt.text()でテキストの変更をしている場合
    • DOMNodeInsertedで監視
    • srcElementはTextになる
    • 変更前と同じテキストが設定されてもイベントが発生する
  • innerHTMLでテキストの変更をしている場合
    • DOMSubtreeModifiedで監視
    • srcElementは監視対象のElementになる。(ここではHTMLHeadingElement)
    • 変更前と異なるテキストが設定されたときだけイベントが発生する

*1:Windows7上のGoogle Chrome 16.0.912.13 における挙動です。

*2:実際はremoveChild/appendChildしてるっぽい