Como se mencionó en varias otras respuestas, los eventos de mutación se han desaprobado, por lo que debería usar MutationObserver en su lugar. Como nadie ha dado detalles al respecto todavía, aquí va ...
API de JavaScript básica
La API de MutationObserver es bastante simple. No es tan simple como los eventos de mutación, pero aún está bien.
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') {
console.log(list[i]);
}
}
});
}
var observer = new MutationObserver(callback);
var targetNode = document.body;
observer.observe(targetNode, { childList: true, subtree: true });
<script>
setTimeout(function() {
var $el = document.createElement('select');
document.body.appendChild($el);
}, 500);
</script>
Analicemos eso.
var observer = new MutationObserver(callback);
Esto crea al observador. El observador aún no está mirando nada; aquí es donde se adjunta el detector de eventos.
observer.observe(targetNode, { childList: true, subtree: true });
Esto hace que el observador se ponga en marcha. El primer argumento es el nodo en el que el observador observará los cambios. El segundo argumento son las opciones de qué estar atento .
childList
significa que quiero ver si se agregan o eliminan elementos secundarios.
subtree
es un modificador que se extiende childList
para observar cambios en cualquier parte del subárbol de este elemento (de lo contrario, solo miraría los cambios directamente dentro targetNode
).
Las otras dos opciones principales además childList
son attributes
y characterData
, lo que significa cómo suenan. Debes usar uno de esos tres.
function callback(records) {
records.forEach(function (record) {
Las cosas se ponen un poco complicadas dentro de la devolución de llamada. La devolución de llamada recibe una matriz de MutationRecord s. Cada MutationRecord puede describir varios cambios de un tipo ( childList
, attributes
o characterData
). Como solo le dije al observador que estuviera atento childList
, no me molestaré en verificar el tipo.
var list = record.addedNodes;
Aquí mismo tomo una NodeList de todos los nodos secundarios que se agregaron. Esto estará vacío para todos los registros donde no se agregan nodos (y puede haber muchos registros de este tipo).
A partir de ahí, recorro los nodos agregados y encuentro los que son <select>
elementos.
Nada realmente complejo aquí.
jQuery
... pero pediste jQuery. Multa.
(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);
Esto crea un nuevo evento llamado domNodeInserted
, usando la API de eventos especiales de jQuery . Puedes usarlo así:
$(document).on("domNodeInserted", "select", function () {
$(this).combobox();
});
Personalmente, sugeriría buscar una clase porque algunas bibliotecas crearán select
elementos con fines de prueba.
Naturalmente, también puede utilizar .off("domNodeInserted", ...)
o ajustar la visualización pasando datos como este:
$(document.body).on("domNodeInserted", "select.test", {
attributes: true,
subtree: false
}, function () {
$(this).combobox();
});
Esto activaría la comprobación de la apariencia de un select.test
elemento cada vez que cambiaran los atributos de los elementos directamente dentro del cuerpo.
Puede verlo en vivo a continuación o en jsFiddle .
(function($) {
$(document).on("domNodeInserted", "select", function() {
console.log(this);
});
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
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>
Nota
Este código jQuery es una implementación bastante básica. No se activa en los casos en que las modificaciones en otros lugares hacen que su selector sea válido.
Por ejemplo, suponga que su selector es .test select
y el documento ya tiene una extensión <select>
. Agregar la clase test
a <body>
hará que el selector sea válido, pero debido a que solo verifico record.target
y record.addedNodes
, el evento no se activará. El cambio tiene que ocurrir en el elemento que desea seleccionar.
Esto podría evitarse consultando el selector siempre que ocurran mutaciones. Elegí no hacer eso para evitar causar eventos duplicados para elementos que ya habían sido manejados. Tratar adecuadamente con combinadores hermanos adyacentes o generales complicaría aún más las cosas.
Para obtener una solución más completa, consulte https://github.com/pie6k/jquery.initialize , como se menciona en la respuesta de Damien Ó Ceallaigh . Sin embargo, el autor de esa biblioteca ha anunciado que la biblioteca es antigua y sugiere que no debería usar jQuery para esto.
$(select).ready(function() { });