javascript - que - innerhtml
Detecta cambios en el DOM (5)
Quiero ejecutar una función cuando algunos div o input se agregan al html. es posible?
Por ejemplo, se agrega una entrada de texto, luego se debe llamar a la función.
Este es el último enfoque hasta ahora, con el código más pequeño:
IE9 +, FF, Webkit:
Usando MutationObserver y recurriendo a los eventos de mutación obsoletos si es necesario:
(Ejemplo a continuación si solo para cambios DOM relacionados con nodos añadidos o eliminados)
var observeDOM = (function(){
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver,
eventListenerSupported = window.addEventListener;
return function(obj, callback){
if( MutationObserver ){
// define a new observer
var obs = new MutationObserver(function(mutations, observer){
if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
callback();
});
// have the observer observe foo for changes in children
obs.observe( obj, { childList:true, subtree:true });
}
else if( eventListenerSupported ){
obj.addEventListener(''DOMNodeInserted'', callback, false);
obj.addEventListener(''DOMNodeRemoved'', callback, false);
}
};
})();
// Observe a specific DOM element:
observeDOM( document.getElementById(''dom_element'') ,function(){
console.log(''dom changed'');
});
Actualización de 2015, el nuevo MutationObserver
es compatible con los navegadores modernos:
Chrome 18+, Firefox 14+, IE 11+, Safari 6+
Si necesita apoyar a las personas mayores, puede tratar de recurrir a otros enfoques como los que se mencionan en esta respuesta de 5 (!) Años a continuación. Hay dragones. Disfruta :)
¿Alguien más está cambiando el documento? Porque si tienes control total sobre los cambios, solo necesitas crear tu propia API domChanged
, con una función o evento personalizado, y activarla / llamarla a todos lados donde modifiques las cosas.
DOM Level-2 tiene tipos de eventos Mutation , pero la versión anterior de IE no lo admite. Tenga en cuenta que los eventos de mutación están en desuso en la especificación de eventos DOM3 y tienen una penalización de rendimiento .
Puedes intentar emular el evento de mutación con onpropertychange
en IE (y recurrir al enfoque de fuerza bruta si no hay onpropertychange
disponible).
Para un DomChange completo, un intervalo podría ser una sobre-muerte. Imagine que necesita almacenar el estado actual de todo el documento y examinar cada propiedad de cada elemento para que sea el mismo.
Tal vez si solo está interesado en los elementos y su orden (como mencionó en su pregunta), un getElementsByTagName("*")
puede funcionar. Esto se disparará automáticamente si agrega un elemento, elimina un elemento, reemplaza elementos o cambia la estructura del documento.
Escribí una prueba de concepto:
(function (window) {
var last = +new Date();
var delay = 100; // default delay
// Manage event queue
var stack = [];
function callback() {
var now = +new Date();
if (now - last > delay) {
for (var i = 0; i < stack.length; i++) {
stack[i]();
}
last = now;
}
}
// Public interface
var onDomChange = function (fn, newdelay) {
if (newdelay) delay = newdelay;
stack.push(fn);
};
// Naive approach for compatibility
function naive() {
var last = document.getElementsByTagName(''*'');
var lastlen = last.length;
var timer = setTimeout(function check() {
// get current state of the document
var current = document.getElementsByTagName(''*'');
var len = current.length;
// if the length is different
// it''s fairly obvious
if (len != lastlen) {
// just make sure the loop finishes early
last = [];
}
// go check every element in order
for (var i = 0; i < len; i++) {
if (current[i] !== last[i]) {
callback();
last = current;
lastlen = len;
break;
}
}
// over, and over, and over again
setTimeout(check, delay);
}, delay);
}
//
// Check for mutation events support
//
var support = {};
var el = document.documentElement;
var remain = 3;
// callback for the tests
function decide() {
if (support.DOMNodeInserted) {
window.addEventListener("DOMContentLoaded", function () {
if (support.DOMSubtreeModified) { // for FF 3+, Chrome
el.addEventListener(''DOMSubtreeModified'', callback, false);
} else { // for FF 2, Safari, Opera 9.6+
el.addEventListener(''DOMNodeInserted'', callback, false);
el.addEventListener(''DOMNodeRemoved'', callback, false);
}
}, false);
} else if (document.onpropertychange) { // for IE 5.5+
document.onpropertychange = callback;
} else { // fallback
naive();
}
}
// checks a particular event
function test(event) {
el.addEventListener(event, function fn() {
support[event] = true;
el.removeEventListener(event, fn, false);
if (--remain === 0) decide();
}, false);
}
// attach test events
if (window.addEventListener) {
test(''DOMSubtreeModified'');
test(''DOMNodeInserted'');
test(''DOMNodeRemoved'');
} else {
decide();
}
// do the dummy test
var dummy = document.createElement("div");
el.appendChild(dummy);
el.removeChild(dummy);
// expose
window.onDomChange = onDomChange;
})(window);
Uso:
onDomChange(function(){
alert("The Times They Are a-Changin''");
});
Esto funciona en IE 5.5+, FF 2+, Chrome, Safari 3+ y Opera 9.6+
¿Qué hay de extender un jquery para esto?
(function () {
var ev = new $.Event(''remove''),
orig = $.fn.remove;
var evap = new $.Event(''append''),
origap = $.fn.append;
$.fn.remove = function () {
$(this).trigger(ev);
return orig.apply(this, arguments);
}
$.fn.append = function () {
$(this).trigger(evap);
return origap.apply(this, arguments);
}
})();
$(document).on(''append'', function (e) { /*write your logic here*/ });
$(document).on(''remove'', function (e) { /*write your logic here*/ ) });
Jquery 1.9+ ha creado soporte para esto (no he escuchado que lo prueben).
Recientemente escribí un complemento que hace exactamente eso: jquery.initialize
Lo usa de la misma manera que .each
función
$(".some-element").initialize( function(){
$(this).css("color", "blue");
});
La diferencia con .each
es que toma su selector, en este caso, .some-element
y espera nuevos elementos con este selector en el futuro; si se agrega ese elemento, también se inicializará.
En nuestro caso, la función de inicialización simplemente cambia el color del elemento a azul. Entonces, si agregamos un nuevo elemento (no importa si tiene ajax o incluso un inspector F12 o algo similar) como:
$("<div/>").addClass(''some-element'').appendTo("body"); //new element will have blue color!
El complemento lo iniciará instantáneamente. Además, el complemento se asegura de que un elemento se inicialice solo una vez. Entonces, si agrega un elemento, luego lo .detach()
del cuerpo y luego lo vuelve a agregar, no se inicializará nuevamente.
$("<div/>").addClass(''some-element'').appendTo("body").detach()
.appendTo(".some-container");
//initialized only once
El complemento se basa en MutationObserver
: funcionará en IE9 y 10 con dependencias, tal como se detalla en la jquery.initialize .
o simplemente puedes crear tu propio evento , que se ejecuta en todas partes
$("body").on("domChanged", function () {
//dom is changed
});
$(".button").click(function () {
//do some change
$("button").append("<span>i am the new change</span>");
//fire event
$("body").trigger("domChanged");
});
Ejemplo completo http://jsfiddle.net/hbmaam/Mq7NX/