javascript - example - jQuery "en crear" evento para elementos creados dinĂ¡micamente
kendo combobox angular (10)
Como se mencionó en varias otras respuestas, los eventos de mutación se han desaprobado, por lo que debe usar MutationObserver en su lugar. Como nadie ha dado ningún detalle al respecto, aquí va ...
API básica de JavaScript
La API para MutationObserver es bastante simple. No es tan simple como los eventos de mutación, pero todavía 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'') {
// 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>
Vamos a romper eso.
var observer = new MutationObserver(callback);
Esto crea el observador. El observador no está mirando nada todavía; esto es justo donde el oyente del evento se apega.
observer.observe(targetNode, { childList: true, subtree: true });
Esto hace que el observador arranque. El primer argumento es el nodo que el observador observará para ver si hay cambios. El segundo argumento es las opciones de qué mirar . childList
significa que quiero ver si se agregan o eliminan elementos secundarios. El subtree
es un modificador que extiende childList
para observar los cambios en cualquier lugar en el subárbol de este elemento (de lo contrario, solo miraría los cambios directamente dentro de <body>
). Otras dos posibilidades importantes son los attributes
y characterData
, que significan cómo suenan.
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 childList
, no me molestaré en verificar el tipo.
var list = record.addedNodes;
Justo aquí tomé una NodeList de todos los nodos secundarios que se agregaron. Esto estará vacío para todos los registros donde los nodos no se agregan (y puede haber muchos de dichos registros).
A partir de ahí, recorro los nodos añadidos y encuentro alguno que sea elementos <select>
.
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
, utilizando la API de eventos especiales de jQuery . Puedes usarlo así:
$(document).on("domNodeInserted", "select", function () {
$(this).combobox();
});
Yo personalmente sugiero buscar una clase porque algunas bibliotecas crearán elementos select
para fines de prueba.
Naturalmente, también puede usar .off("domNodeInserted", ...)
o ajustar la visualización al pasar datos como este:
$(document.body).on("domNodeInserted", "select.test", {
attributes: true,
subtree: false
}, function () {
$(this).combobox();
});
Esto select.test
comprobación de la aparición de un elemento select.test
siempre que los atributos cambien para elementos directamente dentro del cuerpo.
Puedes verlo en vivo debajo o en 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>
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, supongamos que su selector es .test select
y el documento ya tiene una <select>
. Agregar la test
clase a <body>
hará que el selector sea válido, pero como solo record.target
y record.addedNodes
, el evento no se record.addedNodes
. El cambio tiene que pasarle al elemento que desea seleccionar.
Esto podría evitarse preguntando por el selector cada vez que ocurran mutaciones. Elegí no hacer eso para evitar causar eventos duplicados para elementos que ya se habían manejado. Hacer frente adecuadamente a los combinadores de hermanos adjacent o generales haría las cosas aún más complicadas.
Para obtener una solución más completa, consulte https://github.com/pie6k/jquery.initialize , como se menciona en la answer Damien Ó Ceallaigh .
Necesito poder crear dinámicamente el elemento <select>
y convertirlo en jQuery .combobox()
. Este debería ser un evento de creación de elemento, a diferencia de un evento de "clic", en cuyo caso podría usar jQuery .on()
.
Entonces, ¿existe algo como esto?
$(document).on("create", "select", function() {
$(this).combobox();
}
Soy reacio a usar livequery, porque está muy desactualizado.
ACTUALIZACIÓN La mencionada selección / cuadro combinado se carga a través de ajax en un jQuery colorbox (ventana modal), por lo tanto, el problema: solo puedo iniciar el cuadro combinado usando colorbox onComplete
. Sin embargo, al cambiar un cuadro combinado se debe crear dinámicamente / seleccionar cuadro combinado, por lo que necesito una forma más genérica para detectar la creación de un elemento ( select
en este caso).
ACTUALIZACIÓN2 Para tratar de explicar el problema aún más: tengo elementos select/combobox
creados recursivamente, también hay un montón de código de inicio dentro de .combobox()
, por lo tanto, si utilizo un enfoque clásico, como en la respuesta de @ bipen , mi código se inflaría a niveles insanos. Espero que esto explique mejor el problema.
ACTUALIZACIÓN3 Gracias a todos, ahora entiendo que desde la DOMNodeInserted
de DOMNodeInserted
hay un vacío en la mutación DOM y no hay solución para este problema. Tendré que replantear mi aplicación.
Esto podría hacerse con DOM4 MutationObservers
pero solo funcionará en Firefox 14 + / Chrome 18+ (por ahora).
Sin embargo, hay un " hack épico " (¡las palabras del autor no mías!) Que funciona en todos los navegadores que admiten animaciones CSS3 que son: IE10, Firefox 5+, Chrome 3+, Opera 12, Android 2.0+, Safari 4+. Vea la demo del blog. El truco consiste en utilizar un evento de animación CSS3 con un nombre determinado que se observa y se actúa en JavaScript.
Hay un complemento, adampietrasiak/jquery.initialize , que está basado en MutationObserver
que lo logra de forma simple.
$.initialize(".some-element", function() {
$(this).css("color", "blue");
});
Para mí, atarme al cuerpo no funciona. La vinculación al documento con jQuery.bind () sí lo hace.
$(document).bind(''DOMNodeInserted'',function(e){
var target = e.target;
});
Puede on
el evento DOMNodeInserted
obtener un evento cuando su código lo agregue al documento.
$(''body'').on(''DOMNodeInserted'', ''select'', function () {
//$(this).combobox();
});
$(''<select>'').appendTo(''body'');
$(''<select>'').appendTo(''body'');
Fiddled aquí: http://jsfiddle.net/Codesleuth/qLAB2/3/
EDITAR: después de leer todo, solo necesito verificar que DOMNodeInserted
no cause problemas en todos los navegadores. Esta pregunta de 2010 sugiere que IE no es compatible con el evento, así que pruébalo si puedes.
Ver aquí: [link] ¡Advertencia! el tipo de evento DOMNodeInserted se define en esta especificación para referencia e integridad, pero esta especificación deprecates el uso de este tipo de evento.
Puede usar el evento de mutación DOMNodeInserted
(no necesita delegación):
$(''body'').on(''DOMNodeInserted'',function(e){
var target = e.target; //inserted element;
});
EDITAR: los eventos de mutación están en desuso , use el observador de mutación en su lugar
Si está usando angularjs puede escribir su propia directiva. Tuve el mismo problema con bootstrapSwitch. Tengo que llamar a $("[name=''my-checkbox'']").bootstrapSwitch();
en javascript, pero mi objeto de entrada html no se creó en ese momento. Así que escribo una directiva propia y creo el elemento de entrada con <input type="checkbox" checkbox-switch>
En la directiva compilo el elemento para obtener acceso a través de javascript y ejecuto el comando jquery (como su .combobox()
). Muy importante es eliminar el atributo. De lo contrario, esta directiva se llamará a sí misma y usted debe construir un ciclo
app.directive("checkboxSwitch", function($compile) {
return {
link: function($scope, element) {
var input = element[0];
input.removeAttribute("checkbox-switch");
var inputCompiled = $compile(input)($scope.$parent);
inputCompiled.bootstrapSwitch();
}
}
});
Solo se me ocurrió esta solución que parece resolver todos mis problemas con Ajax.
Para los eventos listos ahora uso esto:
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'');
});
Y para los eventos disparadores normales ahora uso esto:
$(document).on(''click'', ''.foo'', function () {
//some action
$(this).css(''background'', ''pink'');
});
Una forma, que parece confiable (aunque probada solo en Firefox y Chrome) es usar JavaScript para escuchar el evento animationend
(o su evento camelCased, y prefixed, sibling animationEnd
), y aplicar una vida corta (en la demo 0.01 segundos) animación al tipo de elemento que planea agregar. Esto, por supuesto, no es un evento onCreate
, sino que aproxima (en navegadores compatibles ) un tipo de evento onInsertion
; la siguiente es una prueba de concepto:
$(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
});
Con el siguiente HTML:
<div class="wrapper">
<button class="add">add a div element</button>
</div>
Y (abreviado, versiones prefijadas eliminadas aunque presentes en el Fiddle, a continuación) 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;
}
Obviamente, el CSS puede ajustarse para adaptarse a la ubicación de los elementos relevantes, así como también al selector utilizado en jQuery (realmente debe estar lo más cerca posible del punto de inserción).
Documentación de los nombres de los eventos:
Mozilla | animationend
Microsoft | MSAnimationEnd
Opera | oanimationend
Webkit | webkitAnimationEnd
W3C | animationend
Referencias
crea un <select>
con id, añádelo al documento ... y llama a .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();
esto debería hacer el truco ... puedes ocultar el seleccionar si quieres y después mostrarlo en la .bobox ... o bien usar find ...
$(document).find(''select'').combobox() //though this is not good performancewise