[javascript] jQuery "on create" event for dynamically-created elements

As mentioned in several other answers, mutation events have been deprecated, so you should use MutationObserver instead. Since nobody has given any details on that yet, here it goes...

Basic JavaScript API

The API for MutationObserver is fairly simple. It's not quite as simple as the mutation events, but it's still okay.

_x000D_
_x000D_
function callback(records) {_x000D_
  records.forEach(function (record) {_x000D_
    var list = record.addedNodes;_x000D_
    var i = list.length - 1;_x000D_
    _x000D_
 for ( ; i > -1; i-- ) {_x000D_
   if (list[i].nodeName === 'SELECT') {_x000D_
     // Insert code here..._x000D_
     console.log(list[i]);_x000D_
   }_x000D_
 }_x000D_
  });_x000D_
}_x000D_
_x000D_
var observer = new MutationObserver(callback);_x000D_
_x000D_
var targetNode = document.body;_x000D_
_x000D_
observer.observe(targetNode, { childList: true, subtree: true });
_x000D_
<script>_x000D_
  // For testing_x000D_
  setTimeout(function() {_x000D_
    var $el = document.createElement('select');_x000D_
    document.body.appendChild($el);_x000D_
  }, 500);_x000D_
</script>
_x000D_
_x000D_
_x000D_

Let's break that down.

var observer = new MutationObserver(callback);

This creates the observer. The observer isn't watching anything yet; this is just where the event listener gets attached.

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

This makes the observer start up. The first argument is the node that the observer will watch for changes on. The second argument is the options for what to watch for.

  • childList means I want to watch for child elements being added or removed.
  • subtree is a modifier that extends childList to watch for changes anywhere in this element's subtree (otherwise, it would just look at changes directly within targetNode).

The other two main options besides childList are attributes and characterData, which mean about what they sound like. You must use one of those three.

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

Things get a little tricky inside the callback. The callback receives an array of MutationRecords. Each MutationRecord can describe several changes of one type (childList, attributes, or characterData). Since I only told the observer to watch for childList, I won't bother checking the type.

var list = record.addedNodes;

Right here I grab a NodeList of all the child nodes that were added. This will be empty for all the records where nodes aren't added (and there may be many such records).

From there on, I loop through the added nodes and find any that are <select> elements.

Nothing really complex here.

jQuery

...but you asked for jQuery. Fine.

(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);

This creates a new event called domNodeInserted, using the jQuery special events API. You can use it like so:

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

I would personally suggest looking for a class because some libraries will create select elements for testing purposes.

Naturally, you can also use .off("domNodeInserted", ...) or fine-tune the watching by passing in data like this:

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

This would trigger checking for the appearance of a select.test element whenever attributes changed for elements directly inside the body.

You can see it live below or on jsFiddle.

_x000D_
_x000D_
(function($) {_x000D_
  $(document).on("domNodeInserted", "select", function() {_x000D_
    console.log(this);_x000D_
    //$(this).combobox();_x000D_
  });_x000D_
})(jQuery);
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
_x000D_
<script>_x000D_
  // For testing_x000D_
  setTimeout(function() {_x000D_
    var $el = document.createElement('select');_x000D_
    document.body.appendChild($el);_x000D_
  }, 500);_x000D_
</script>_x000D_
_x000D_
<script>_x000D_
  (function($) {_x000D_
_x000D_
    var observers = [];_x000D_
_x000D_
    $.event.special.domNodeInserted = {_x000D_
_x000D_
      setup: function setup(data, namespaces) {_x000D_
        var observer = new MutationObserver(checkObservers);_x000D_
_x000D_
        observers.push([this, observer, []]);_x000D_
      },_x000D_
_x000D_
      teardown: function teardown(namespaces) {_x000D_
        var obs = getObserverData(this);_x000D_
_x000D_
        obs[1].disconnect();_x000D_
_x000D_
        observers = $.grep(observers, function(item) {_x000D_
          return item !== obs;_x000D_
        });_x000D_
      },_x000D_
_x000D_
      remove: function remove(handleObj) {_x000D_
        var obs = getObserverData(this);_x000D_
_x000D_
        obs[2] = obs[2].filter(function(event) {_x000D_
          return event[0] !== handleObj.selector && event[1] !== handleObj.handler;_x000D_
        });_x000D_
      },_x000D_
_x000D_
      add: function add(handleObj) {_x000D_
        var obs = getObserverData(this);_x000D_
_x000D_
        var opts = $.extend({}, {_x000D_
          childList: true,_x000D_
          subtree: true_x000D_
        }, handleObj.data);_x000D_
_x000D_
        obs[1].observe(this, opts);_x000D_
_x000D_
        obs[2].push([handleObj.selector, handleObj.handler]);_x000D_
      }_x000D_
    };_x000D_
_x000D_
    function getObserverData(element) {_x000D_
      var $el = $(element);_x000D_
_x000D_
      return $.grep(observers, function(item) {_x000D_
        return $el.is(item[0]);_x000D_
      })[0];_x000D_
    }_x000D_
_x000D_
    function checkObservers(records, observer) {_x000D_
      var obs = $.grep(observers, function(item) {_x000D_
        return item[1] === observer;_x000D_
      })[0];_x000D_
_x000D_
      var triggers = obs[2];_x000D_
_x000D_
      var changes = [];_x000D_
_x000D_
      records.forEach(function(record) {_x000D_
        if (record.type === 'attributes') {_x000D_
          if (changes.indexOf(record.target) === -1) {_x000D_
            changes.push(record.target);_x000D_
          }_x000D_
_x000D_
          return;_x000D_
        }_x000D_
_x000D_
        $(record.addedNodes).toArray().forEach(function(el) {_x000D_
          if (changes.indexOf(el) === -1) {_x000D_
            changes.push(el);_x000D_
          }_x000D_
        })_x000D_
      });_x000D_
_x000D_
      triggers.forEach(function checkTrigger(item) {_x000D_
        changes.forEach(function(el) {_x000D_
          var $el = $(el);_x000D_
_x000D_
          if ($el.is(item[0])) {_x000D_
            $el.trigger('domNodeInserted');_x000D_
          }_x000D_
        });_x000D_
      });_x000D_
    }_x000D_
_x000D_
  })(jQuery);_x000D_
</script>
_x000D_
_x000D_
_x000D_


Note

This jQuery code is a fairly basic implementation. It does not trigger in cases where modifications elsewhere make your selector valid.

For example, suppose your selector is .test select and the document already has a <select>. Adding the class test to <body> will make the selector valid, but because I only check record.target and record.addedNodes, the event would not fire. The change has to happen to the element you wish to select itself.

This could be avoided by querying for the selector whenever mutations happen. I chose not to do that to avoid causing duplicate events for elements that had already been handled. Properly dealing with adjacent or general sibling combinators would make things even trickier.

For a more comprehensive solution, see https://github.com/pie6k/jquery.initialize, as mentioned in Damien Ó Ceallaigh's answer. However, the author of that library has announced that the library is old and suggests that you shouldn't use jQuery for this.

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to jquery

How to make a variable accessible outside a function? Jquery assiging class to th in a table Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Getting all files in directory with ajax Bootstrap 4 multiselect dropdown Cross-Origin Read Blocking (CORB) bootstrap 4 file input doesn't show the file name Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource how to remove json object key and value.?

Examples related to combobox

How to set combobox default value? PHP code to get selected text of a combo box How to add items to a combobox in a form in excel VBA? How add items(Text & Value) to ComboBox & read them in SelectedIndexChanged (SelectedValue = null) Get Selected value of a Combobox jQuery "on create" event for dynamically-created elements How to get the selected item of a combo box to a string variable in c# HTML combo box with option to type an entry twitter bootstrap autocomplete dropdown / combobox with Knockoutjs C# winforms combobox dynamic autocomplete