JavaScript
jQuery
chrome-extension

動的に追加されるDOM要素に対応する方法あれこれ

More than 5 years have passed since last update.

Chrome拡張を作っていると,最近のモダンなWebサイトが後から動的にDOM要素を追加してきて困ることがある.毎度対処方法をぐぐっているので,ここにまとめておく.

追加される要素にイベントハンドラを付加したい

jQueryのonメソッドにselectorを渡すことで,あとから動的に追加された子要素に対してもイベントを発火することができる.
liveメソッドでも同様のことが出来たが,1.7で廃止された.

$("#parent").on("click", ".child", function(){ console.log("click");});
$("#parent").append("<span class='child'>Click Here</span>");

要素が追加されたことそのものを検出する

DOMNodeInserted: 非推奨

要素が追加されたときに発火するDOMNodeInsertedというイベントが存在する.少し前に試した時はモダンなブラウザでは動いた.
DOMに要素が挿入される度にイベントを起こすDOMNodeInsertedの扱い方 - 三等兵
にあるように,処理をブロックしないように非同期処理を行うべきだろう.
現在,DOMNodeInsertedをはじめとするMutation eventsはパフォーマンスと安定性の問題によってdeprecatedとなってしまっているので,今後どうなるかはわからない.

MutationObserver

DOM4ではMutation eventsに代わってMutationObserverが導入された.
Mutation eventsで指摘されていたパフォーマンスや安定性の問題に対応するため,インターフェースはやや複雑になっている.

CSSアニメーションを利用したワークアラウンド

基本的なアイディアは,CSSを使って追加を検出したい要素にkeyframeアニメーションを登録しておき,そのanimationstartイベントを監視するというもの.自分のユースケースではChromeでのみ動いてくれれば良いので,以下にWebKit向けの例を示す.

fake_animation.css
@-webkit-keyframes elementInserted {  
  0% {opacity: 0;}
  100% {opacity: 1;}
}

.target-element {
  -webkit-animation: elementInserted 0.001s 1;
}
detect_element_inserted.js
document.addEventListener('webkitAnimationStart', function(event){
  if (event.animationName == 'elementInserted') {
    // do something
  }
}, true);

出典: Back Alley Coder — I Want a DAMNodeInserted Event!

おまけ: AutoPagerizeによるページ読み込みを検出する

今も動くかわからないのだけど,AutoPagerizeは要素を読み込んだ時に AutoPagerize_DOMNodeInserted イベントを投げてくれたらしい.以下に使い方の例がある.面白いと思ったのでついでに紹介しておく.
AutoPagerize_DOMNodeInserted 使用時のテンプレート - twwp