web-dev-qa-db-ja.com

動的に作成された要素のjQuery「on create」イベント

<select>要素を動的に作成し、jQuery .combobox()に変換できるようにする必要があります。これは、jQuery .on()を使用できる「クリック」イベントではなく、要素作成イベントである必要があります。

それで、このようなものは存在しますか?

$(document).on("create", "select", function() {
    $(this).combobox();
}

Livequeryは非常に時代遅れなので、使用することに消極的です。

UPDATE前述の選択/コンボボックスは、ajaxを介してjQueryカラーボックス(モーダルウィンドウ)にロードされるため、問題-カラーボックスを使用してコンボボックスのみを開始できますonCompleteただし、1つのコンボボックスを変更すると、別のselect/comboboxを動的に作成する必要があるため、要素の作成を検出するより一般的な方法が必要です(この場合はselect)。

UPDATE2さらに問題を説明するために-select/combobox要素を再帰的に作成しました。また、.combobox()内に多くの開始コードがあります。 @ bipen's answer のような古典的なアプローチを使用した場合、私のコードは非常識なレベルに膨らみます。これが問題をよりよく説明することを願っています。

UPDATE3皆さん、ありがとうございます。DOMNodeInsertedが非推奨になったため、DOMミューテーションにボイドが残っており、この問題の解決策がないことを理解しました。アプリケーションを再考する必要があります。

70
Caballero

onDOMNodeInsertedイベントを使用すると、コードによってドキュメントに追加されたときのイベントを取得できます。

$('body').on('DOMNodeInserted', 'select', function () {
      //$(this).combobox();
});

$('<select>').appendTo('body');
$('<select>').appendTo('body');

ここで調整: http://jsfiddle.net/Codesleuth/qLAB2/3/

編集:DOMNodeInsertedを再確認するだけで、ブラウザ間で問題が発生することはありません。 この質問 2010年からIEはイベントをサポートしていないことが示唆されているため、可能であればテストしてください。

こちらをご覧ください: [link]警告! DOMNodeInsertedイベントタイプは、参照と完全性のためにこの仕様で定義されていますが、この仕様 deprecates このイベントタイプの使用

52
Codesleuth

DOMNodeInserted突然変異イベントを使用できます(委任は不要です):

$('body').on('DOMNodeInserted',function(e){
    var target = e.target; //inserted element;
});

編集: 突然変異イベント aredeprecated、代わりに mutation observer を使用

19
Yukulélé

他のいくつかの回答で述べたように、 mutation events は廃止されているため、代わりに MutationObserver を使用する必要があります。まだ誰も詳細を教えていないので、ここに行きます...

基本的なJavaScript API

MutationObserverのAPIは非常にシンプルです。突然変異イベントほど単純ではありませんが、それでも大丈夫です。

function callback(records) {
  records.forEach(function (record) {
    var list = record.addedNodes;
    var i = list.length - 1;
    
        for ( ; i > -1; i-- ) {
          if (list[i].nodeName === 'SELECT') {
            // Insert code here...
            console.log(list[i]);
          }
        }
  });
}

var observer = new MutationObserver(callback);

var targetNode = document.body;

observer.observe(targetNode, { childList: true, subtree: true });
<script>
  // For testing
  setTimeout(function() {
    var $el = document.createElement('select');
    document.body.appendChild($el);
  }, 500);
</script>

それを分解しましょう。

var observer = new MutationObserver(callback);

これにより、オブザーバーが作成されます。オブザーバーはまだ何も見ていません。これは、イベントリスナーが接続される場所です。

observer.observe(targetNode, { childList: true, subtree: true });

これにより、オブザーバーが起動します。最初の引数は、オブザーバーが変更を監視するノードです。 2番目の引数は、 監視対象のオプション です。 childListは、追加または削除される子要素を監視することを意味します。 subtreechildListを拡張した修飾子であり、この要素のサブツリーの任意の場所の変更を監視します(そうでない場合は、<body>内で直接変更を確認します)。他の2つの重要な可能性はattributescharacterDataです。これは、それらがどのように聞こえるかを意味します。

function callback(records) {
  records.forEach(function (record) {

コールバック内では、少し注意が必要です。コールバックは MutationRecord sの配列を受け取ります。各MutationRecordは、1つのタイプ(childListattributes、またはcharacterData)のいくつかの変更を記述することができます。オブザーバーにchildListを監視するように指示しただけなので、タイプの確認は気にしません。

var list = record.addedNodes;

ここで、追加されたすべての子ノードのNodeListを取得します。これは、ノードが追加されていないすべてのレコードで空になります(そのようなレコードが多数ある場合があります)。

そこから、追加されたノードをループし、<select>要素であるものを見つけます。

ここでは本当に複雑なことは何もありません。

jQuery

...しかし、jQueryを要求しました。いいよ.

(function($) {

  var observers = [];

  $.event.special.domNodeInserted = {

    setup: function setup(data, namespaces) {
      var observer = new MutationObserver(checkObservers);

      observers.Push([this, observer, []]);
    },

    teardown: function teardown(namespaces) {
      var obs = getObserverData(this);

      obs[1].disconnect();

      observers = $.grep(observers, function(item) {
        return item !== obs;
      });
    },

    remove: function remove(handleObj) {
      var obs = getObserverData(this);

      obs[2] = obs[2].filter(function(event) {
        return event[0] !== handleObj.selector && event[1] !== handleObj.handler;
      });
    },

    add: function add(handleObj) {
      var obs = getObserverData(this);

      var opts = $.extend({}, {
        childList: true,
        subtree: true
      }, handleObj.data);

      obs[1].observe(this, opts);

      obs[2].Push([handleObj.selector, handleObj.handler]);
    }
  };

  function getObserverData(element) {
    var $el = $(element);

    return $.grep(observers, function(item) {
      return $el.is(item[0]);
    })[0];
  }

  function checkObservers(records, observer) {
    var obs = $.grep(observers, function(item) {
      return item[1] === observer;
    })[0];

    var triggers = obs[2];

    var changes = [];

    records.forEach(function(record) {
      if (record.type === 'attributes') {
        if (changes.indexOf(record.target) === -1) {
          changes.Push(record.target);
        }

        return;
      }

      $(record.addedNodes).toArray().forEach(function(el) {
        if (changes.indexOf(el) === -1) {
          changes.Push(el);
        }
      })
    });

    triggers.forEach(function checkTrigger(item) {
      changes.forEach(function(el) {
        var $el = $(el);

        if ($el.is(item[0])) {
          $el.trigger('domNodeInserted');
        }
      });
    });
  }

})(jQuery);

これにより、 jQuery特殊イベントAPI を使用して、domNodeInsertedという新しいイベントが作成されます。次のように使用できます。

$(document).on("domNodeInserted", "select", function () {
  $(this).combobox();
});

一部のライブラリはテスト目的でselect要素を作成するため、個人的にクラスを探すことをお勧めします。

当然、.off("domNodeInserted", ...)を使用するか、次のようなデータを渡すことで監視を微調整することもできます。

$(document.body).on("domNodeInserted", "select.test", {
  attributes: true,
  subtree: false
}, function () {
  $(this).combobox();
});

これにより、本体内の要素の属性が直接変更されるたびに、select.test要素の外観のチェックがトリガーされます。

下または jsFiddle でライブで確認できます。

(function($) {
  $(document).on("domNodeInserted", "select", function() {
    console.log(this);
    //$(this).combobox();
  });
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<script>
  // For testing
  setTimeout(function() {
    var $el = document.createElement('select');
    document.body.appendChild($el);
  }, 500);
</script>

<script>
  (function($) {

    var observers = [];

    $.event.special.domNodeInserted = {

      setup: function setup(data, namespaces) {
        var observer = new MutationObserver(checkObservers);

        observers.Push([this, observer, []]);
      },

      teardown: function teardown(namespaces) {
        var obs = getObserverData(this);

        obs[1].disconnect();

        observers = $.grep(observers, function(item) {
          return item !== obs;
        });
      },

      remove: function remove(handleObj) {
        var obs = getObserverData(this);

        obs[2] = obs[2].filter(function(event) {
          return event[0] !== handleObj.selector && event[1] !== handleObj.handler;
        });
      },

      add: function add(handleObj) {
        var obs = getObserverData(this);

        var opts = $.extend({}, {
          childList: true,
          subtree: true
        }, handleObj.data);

        obs[1].observe(this, opts);

        obs[2].Push([handleObj.selector, handleObj.handler]);
      }
    };

    function getObserverData(element) {
      var $el = $(element);

      return $.grep(observers, function(item) {
        return $el.is(item[0]);
      })[0];
    }

    function checkObservers(records, observer) {
      var obs = $.grep(observers, function(item) {
        return item[1] === observer;
      })[0];

      var triggers = obs[2];

      var changes = [];

      records.forEach(function(record) {
        if (record.type === 'attributes') {
          if (changes.indexOf(record.target) === -1) {
            changes.Push(record.target);
          }

          return;
        }

        $(record.addedNodes).toArray().forEach(function(el) {
          if (changes.indexOf(el) === -1) {
            changes.Push(el);
          }
        })
      });

      triggers.forEach(function checkTrigger(item) {
        changes.forEach(function(el) {
          var $el = $(el);

          if ($el.is(item[0])) {
            $el.trigger('domNodeInserted');
          }
        });
      });
    }

  })(jQuery);
</script>

注意

このjQueryコードは、かなり基本的な実装です。他の場所での変更によりセレクタが有効になった場合はトリガーされません。

たとえば、セレクタが.test selectであり、ドキュメントに既に<select>があるとします。クラスtest<body>に追加すると、セレクターが有効になりますが、record.targetおよびrecord.addedNodesのみをチェックするため、イベントは発生しません。変更は、それ自体を選択する要素に発生する必要があります。

これは、突然変異が発生するたびにセレクターを照会することで回避できます。既に処理された要素に対してイベントが重複することを避けるために、私はそうしないことを選択しました。 隣接 または 一般的な兄弟コンビネータ を適切に処理すると、事態はさらに複雑になります。

より包括的なソリューションについては、 https://github.com/pie6k/jquery.initialize を参照してください DamienÓCeallaighanswer =。

8
Andrew Myers

私のすべてのajaxの問題を解決するように思われるこのソリューションを思いついた。

準備完了のイベントでは、これを使用します。

function loaded(selector, callback){
    //trigger after page load.
    $(function () {
        callback($(selector));
    });
    //trigger after page update eg ajax event or jquery insert.
    $(document).on('DOMNodeInserted', selector, function () {
        callback($(this));
    });
}

loaded('.foo', function(el){
    //some action
    el.css('background', 'black');
});

そして、通常のトリガーイベントには、これを使用します。

$(document).on('click', '.foo', function () {
    //some action
    $(this).css('background', 'pink');
});
6
Dieter Gribnitz

これは DOM4 MutationObservers で実行できますが、Firefox 14 +/Chrome 18+(現在)でのみ機能します。

ただし、IE10、Firefox 5以降、Chrome 3以降のCSS3アニメーションをサポートするすべてのブラウザで機能する「 epic hack 」(著者の言葉は私のものではありません!) Opera 12、Android 2.0 +、Safari 4+。ブログの demo を参照してください。このハッキングは、JavaScriptで監視および実行される特定の名前のCSS3アニメーションイベントを使用することです。

4
andyb

プラグイン adampietrasiak/jquery.initialize があり、これは単純にこれを実現するMutationObserverに基づいています。

$.initialize(".some-element", function() {
    $(this).css("color", "blue");
});
4

seems信頼できる(FirefoxとChromeでのみテストされています)1つの方法は、JavaScriptを使用してanimationend(またはそのcamelCased 、接頭辞付きの兄弟animationEnd)イベント、追加する予定の要素タイプに短命(デモでは0.01秒)のアニメーションを適用します。もちろん、これはonCreateイベントではなく、(準拠ブラウザーで)onInsertionタイプのイベントに近似しています。以下は概念実証です。

$(document).on('webkitAnimationEnd animationend MSAnimationEnd oanimationend', function(e){
    var eTarget = e.target;
    console.log(eTarget.tagName.toLowerCase() + ' added to ' + eTarget.parentNode.tagName.toLowerCase());
    $(eTarget).draggable(); // or whatever other method you'd prefer
});

次のHTMLを使用します。

<div class="wrapper">
    <button class="add">add a div element</button>
</div>

そして(以下のFiddleに存在するものの、省略された、prefixed-versions-removed)CSS:

/* vendor-prefixed alternatives removed for brevity */
@keyframes added {
    0% {
        color: #fff;
    }
}

div {
    color: #000;
    /* vendor-prefixed properties removed for brevity */
    animation: added 0.01s linear;
    animation-iteration-count: 1;
}

JS Fiddle demo

明らかに、CSSは、jQueryで使用されるセレクターと同様に、関連する要素の配置に合わせて調整することができます(実際には、可能な限り挿入ポイントに近いはずです)。

イベント名のドキュメント:

Mozilla   |  animationend
Microsoft |  MSAnimationEnd
Opera     |  oanimationend
Webkit    |  webkitAnimationEnd
W3C       |  animationend

参照:

4
David Thomas

私にとって、体への結合は機能しません。 jQuery.bind()を使用してドキュメントにバインドします。

$(document).bind('DOMNodeInserted',function(e){
             var target = e.target;
         });
4
Milan Simek

idで<select>を作成し、ドキュメントに追加し、.comboboxを呼び出します

  var dynamicScript='<select id="selectid"><option value="1">...</option>.....</select>'
  $('body').append(dynamicScript); //append this to the place your wanted.
  $('#selectid').combobox();  //get the id and add .combobox();

これでうまくいくはずです..必要に応じて選択を非表示にし、.comboboxを表示した後、またはfindを使用してください。

 $(document).find('select').combobox() //though this is not good performancewise
0
bipen