Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2010-02-04

DOM Mutation Events は非同期にして使おう

04:35

知ってる人にとってはどうってことない基本的なこと。


DOM 3 Events で deprecated 扱いになった DOM Mutations Events だけど、まだ代替品は無いわけで、これを使わざるをえない。

DOM Mutation Events は同期的なイベントなので、うまく使わないと全体の動作がモッサリしてしまうのが難点。

つまり、ループで DOM ツリーを弄るようなスクリプトだと、ループ中に毎回呼ばれて大変なことになる。

特に同期的動作が必要でない場合は setTimeout を入れてしまったほうがいい。

// イベントリスナーを付ける
document.addEventListener('DOMNodeInserted', nodeInsertedHandler, false);

function nodeInsertedHandler() {
  // ハンドラー内でイベントリスナーを削除する
  document.removeEventListener('DOMNodeInserted', nodeInsertedHandler, false);

  setTimeout(function() { // 内容は別イベントに分ける
    /* 処理 */

    // イベントリスナーを復活させる
    document.addEventListener('DOMNodeInserted', nodeInsertedHandler, false);
  }, 10);
}

これだと何回ループが回っても DOMNodeInserted は最初に1回出るだけ。しかも同期処理中でやってることは setTimeout を置くだけなのであまり重くならない。

もし挿入された要素が使いたいときは適当なスタックを用意。

document.addEventListener('DOMNodeInserted', nodeInsertedHandler, false);

var timer = null;
var nodes = [];
function nodeInsertedHandler(e) {
  nodes.push(e.target);

  if (!timer) {
    timer = setTimeout(function() {
      /* 処理 */
      nodes = []; // スタック初期化
      timer = null; // タイマーを初期化
    }, 50);
  }
}

または

document.addEventListener('DOMNodeInserted', nodeInsertedHandler, false);

var timer;
var nodes = [];
function nodeInsertedHandler(e) {
  nodes.push(e.target);

  clearTimeout(timer);
  timer = setTimeout(function() {
    /* 処理 */
    nodes = [];
  }, 50);
}

後者の場合は、例えば連続してイベントが発生 (キーを押しっぱなしにしたりとか、マウスを動かすなど) してノードを追加するときに、最後のイベントが終わってから 50ms 時間が空けば処理を実行する。debouncing と呼ぶらしい。前者だと、イベントが連続して発生すると、途中に割り込んで処理を実行する。どちらが良いかはその時の状況で使い分けたらいい。

 |