javascript - observer - Evento cuando elemento agregado a la página
mutationobserver jquery (7)
Esto fue discutido previamente aquí: ¿Cómo hacer una acción cuando un elemento se agrega a una página usando Jquery?
El respondedor sugirió activar un evento personalizado siempre que se agregara un div a la página. Sin embargo, estoy escribiendo una extensión de Chrome y no tengo acceso al origen de la página. ¿Cuáles son mis opciones aquí? Supongo que en teoría podría usar setTimeout
para buscar continuamente la presencia del elemento y agregar mi acción si el elemento está allí.
Como los eventos de mutación DOM ahora están en deprecated (vea la nota en la parte inferior) con las últimas especificaciones, y usted no tiene control sobre los elementos insertados porque el código de otra persona los agrega, su única opción es verificarlos continuamente.
function checkDOMChange()
{
// check for any new element being inserted here,
// or a particular node being modified
// call the function again after 100 milliseconds
setTimeout( checkDOMChange, 100 );
}
Una vez que se llame a esta función, se ejecutará cada xxx milisegundos. Elegí 100, que es 1/10 (una décima) de segundo. A menos que necesite capturar elementos en tiempo real, debería ser suficiente.
Editar
Como comentó Ben Davis, la especificación DOM Nivel 4 introduce observadores de mutaciones (también en documentos de Mozilla ), que reemplazan los eventos de mutación en desuso.
Hay una prometedora biblioteca de JavaScript llamada Arrive que parece una excelente manera de empezar a aprovechar los observadores de la mutación una vez que el soporte del navegador se convierte en algo común.
La respuesta real es "usar observadores de mutación" (como se describe en esta pregunta: determinar si un elemento HTML se ha agregado dinámicamente al DOM ), sin embargo, el soporte (específicamente en IE) es limitado ( http://caniuse.com/mutationobserver ) .
Entonces, la respuesta ACTUAL real es "Usar observadores de mutaciones ... eventualmente. Pero por el momento déjate contestar por la respuesta de José Faeti" :)
Mira este plugin que hace exactamente eso - jquery.initialize
Funciona exactamente como cada función, la diferencia es que toma el selector que ha ingresado y está atento a los nuevos elementos agregados en el futuro que coinciden con este selector y los inicializa
Initialize se ve así
$(".some-element").initialize( function(){
$(this).css("color", "blue");
});
Pero ahora, si en la página aparece un nuevo selector de .some-element
, se inicializará al instante.
La forma en que se agrega el nuevo elemento no es importante, no necesita preocuparse por las devoluciones de llamada, etc.
Entonces, si agregas un nuevo elemento como:
$("<div/>").addClass(''some-element'').appendTo("body"); //new element will have blue color!
se inicializará instantáneamente.
El complemento está basado en MutationObserver
Puede usar el complemento livequery
para jQuery. Puede proporcionar una expresión de selector como, por ejemplo:
$("input[type=button].removeItemButton").livequery(function () {
$("#statusBar").text(''You may now remove items.'');
});
Cada vez que se agrega un botón de una clase removeItemButton
, aparece un mensaje en una barra de estado.
En términos de eficiencia, es posible que desee evitar esto, pero en cualquier caso podría aprovechar el complemento en lugar de crear sus propios controladores de eventos.
Respuesta revisada
La respuesta anterior solo pretendía detectar que un elemento se haya agregado al DOM a través del complemento.
Sin embargo, lo más probable es que un enfoque jQuery.on()
sea más apropiado, por ejemplo:
$("#myParentContainer").on(''click'', ''.removeItemButton'', function(){
alert($(this).text() + '' has been removed'');
}
Si tiene contenido dinámico que debe responder a los clics, por ejemplo, es mejor vincular los eventos a un contenedor principal utilizando jQuery.on
.
También puede explotar eventos de animación CSS3 para recibir notificaciones cuando se agrega un elemento específico al DOM: http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/
ETA 24 de abril 17 Quería simplificar esto un poco con algo de magia async
/ await
, ya que lo hace mucho más sucinto:
Usando el mismo promisificado-observable:
const startObservable = (domNode) => {
var targetNode = domNode;
var observerConfig = {
attributes: true,
childList: true,
characterData: true
};
return new Promise((resolve) => {
var observer = new MutationObserver(function (mutations) {
// For the sake of...observation...let''s output the mutation to console to see how this all works
mutations.forEach(function (mutation) {
console.log(mutation.type);
});
resolve(mutations)
});
observer.observe(targetNode, observerConfig);
})
}
Su función de llamada puede ser tan simple como:
const waitForMutation = async () => {
const button = document.querySelector(''.some-button'')
if (button !== null) button.click()
try {
const results = await startObservable(someDomNode)
return results
} catch (err) {
console.error(err)
}
}
Si desea agregar un tiempo de espera, puede usar un patrón Promise.race
simple como se muestra aquí :
const waitForMutation = async (timeout = 5000 /*in ms*/) => {
const button = document.querySelector(''.some-button'')
if (button !== null) button.click()
try {
const results = await Promise.race([
startObservable(someDomNode),
// this will throw after the timeout, skipping
// the return & going to the catch block
new Promise((resolve, reject) => setTimeout(
reject,
timeout,
new Error(''timed out waiting for mutation'')
)
])
return results
} catch (err) {
console.error(err)
}
}
Original
Puedes hacer esto sin bibliotecas, pero tendrías que usar algo de ES6, así que ten en cuenta los problemas de compatibilidad (es decir, si tu audiencia es mayoritariamente Amish, luddite o, lo que es peor, usuarios de IE8)
Primero, usaremos la API MutationObserver para construir un objeto observador. Vamos a envolver este objeto en una promesa, y resolve()
cuando se active la devolución de llamada (h / t davidwalshblog) artículo del blog de david walsh sobre mutaciones :
const startObservable = (domNode) => {
var targetNode = domNode;
var observerConfig = {
attributes: true,
childList: true,
characterData: true
};
return new Promise((resolve) => {
var observer = new MutationObserver(function (mutations) {
// For the sake of...observation...let''s output the mutation to console to see how this all works
mutations.forEach(function (mutation) {
console.log(mutation.type);
});
resolve(mutations)
});
observer.observe(targetNode, observerConfig);
})
}
Entonces, crearemos una generator function
. Si aún no los ha usado, entonces se está perdiendo, pero una breve sinopsis es: se ejecuta como una función de sincronización, y cuando encuentra una expresión de yield <Promise>
, espera de forma no bloqueante para la promesa de cumplirse (los generadores hacen más que esto, pero esto es lo que nos interesa aquí ).
// we''ll declare our DOM node here, too
let targ = document.querySelector(''#domNodeToWatch'')
function* getMutation() {
console.log("Starting")
var mutations = yield startObservable(targ)
console.log("done")
}
Una parte difícil de los generadores es que no "regresan" como una función normal. Entonces, usaremos una función auxiliar para poder usar el generador como una función normal. (de nuevo, h / t a dwb )
function runGenerator(g) {
var it = g(), ret;
// asynchronously iterate over generator
(function iterate(val){
ret = it.next( val );
if (!ret.done) {
// poor man''s "is it a promise?" test
if ("then" in ret.value) {
// wait on the promise
ret.value.then( iterate );
}
// immediate value: just send right back in
else {
// avoid synchronous recursion
setTimeout( function(){
iterate( ret.value );
}, 0 );
}
}
})();
}
Luego, en cualquier momento antes de que ocurra la mutación DOM esperada, simplemente ejecute runGenerator(getMutation)
.
Ahora puede integrar mutaciones DOM en un flujo de control de estilo síncrono. ¿Qué tal eso?