ETA 24 abr 17
Quería simplificar esto un poco con algunos async
/await
magia, ya que lo hace mucho más sucinto:
Usando el mismo prometido-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 Promise.race
patrón 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
Puede hacer esto sin bibliotecas, pero tendría que usar algunas cosas de ES6, así que tenga en cuenta los problemas de compatibilidad (es decir, si su audiencia es principalmente amish, ludita o, peor aún, usuarios de IE8)
Primero, usaremos la API MutationObserver para construir un objeto observador. Envolveremos este objeto en una promesa, yresolve()
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 un generator function
. Si aún no los ha usado, se está perdiendo, pero una breve sinopsis es: se ejecuta como una función de sincronización, y cuando encuentra unyield <Promise>
expresión, espera sin bloqueos la promesa de ser cumplido (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 regular. (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?