2016年5月17日火曜日

DOMNodeInserted DOMNodeRemovedを使って、divとかformとかに子要素が追加されるとか、中身の一部が消されるときにイベントを発火させたい的なお話

  • このエントリーをはてなブックマークに追加

とりあえずタイトルが長いけど、何がしたいかっていうとDOMの監視をしたいっていうこと。
divの中にimgが入ったとかpが入ったとか、formの中にinput hiddenが入ったとか消えたとか云々。
そういうときにフォームのサブミットボタンをアクティブにしたいとかっていう感じ。

で、それを実現するにはformだったら各フォームパーツのclickイベントとかの最後に処理を入れてみたりとか、
divだったらsetInterval使ってDOMの中身を監視するとかどうとか。

けどどっちもぶっちゃけ処理が多くなればなるほど面倒なことになってしまう。
特にフォーム関連なんてパーツが増えるたびに書かないといけないとか面倒ではある。

ってことでDOMNodeInsertedやDOMNodeRemovedっていうイベントを使ってその処理を書きましょう的な。

子要素に何か入ったとか消えたとかを監視したい

想定するのは上の図のような感じで、画像がアップされたら下にあるボタンをアクティブにしたい。

/* click upload button */
$('.img-append-btn').on('click',function(e){
  $('[name="form"]').append('<limg src="hoge.jpg">');
  $('[name="form"]').append('<input type="hidden" name="img" value="hoge.jpg">');
});

/* form observing by insert or remove */
$('[name="form"]').on("DOMNodeInserted DOMNodeRemoved",function(e){
  setTimeout(function(){
    if($('[name="form"]').find('[type="hidden"]').length){
      $(".save-btn").prop("disabled",false);
    }
    else{
      $(".save-btn").prop("disabled");
    }
  },1000);
});
こんな感じ。
なんでsetTimeoutを使うかっていうとinput[type="hidden"]を削除したときに、削除し終わる際に発火するわけではないから。
なんていうかDOMを消すよっていう命令を下したタイミングでDOMNodeRemovedは発火するので、removeされ終わらないっていう問題からsetTimeoutを使う必要がある。

ちなみにDOMSubtreeModifiedっていうのも使えるけど、
これを使うとほぼ確実に「Uncaught RangeError: Maximum call stack size exceeded.」ということでコールスタック(関数呼びすぎ問題)になってしまうので要注意。
なお上記と同じようにsetTimeoutで処理をしてあげればコールスタックは回避できるんだけど、ひたすらタイマー回してるのと変わらないからメモリを喰うから推奨はしない。

ってことでこんな感じで監視ができるのでめっちゃ便利。
特にwebアプリでReactとか使うまでもないのを書く場合とかは便利ではあったりするよっていう感じではあったりする的なみたいな。

Adsense