ETA 24 avril 17
Je voulais simplifier un peu cela avec certains async
/await
magic, car cela le rend beaucoup plus succinct:
En utilisant le même observable promisifié:
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);
})
}
Votre fonction d'appel peut être aussi simple que:
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 vous souhaitez ajouter un délai d'attente, vous pouvez utiliser un Promise.race
modèle simple comme illustré ici :
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
Vous pouvez le faire sans bibliothèques, mais vous devrez utiliser des éléments ES6, alors soyez conscient des problèmes de compatibilité (par exemple, si votre public est principalement des utilisateurs Amish, Luddite ou, pire, IE8)
Tout d'abord, nous utiliserons l' API MutationObserver pour construire un objet observateur. Nous allons envelopper cet objet dans une promesse, et resolve()
quand le rappel est déclenché (h / t davidwalshblog) article du blog de David Walsh sur les mutations :
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);
})
}
Ensuite, nous allons créer un fichier generator function
. Si vous ne les avez pas encore utilisés, alors vous passez à côté - mais un bref résumé est: il fonctionne comme une fonction de synchronisation, et quand il trouve une yield <Promise>
expression, il attend de manière non bloquante que la promesse soit rempli (les générateurs font plus que cela, mais c'est ce qui nous intéresse ici ).
// 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")
}
Une partie délicate des générateurs est qu'ils ne «retournent» pas comme une fonction normale. Nous allons donc utiliser une fonction d'assistance pour pouvoir utiliser le générateur comme une fonction normale. (encore une fois, h / t à 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 );
}
}
})();
}
Ensuite, à tout moment avant que la mutation DOM attendue ne se produise, exécutez simplement runGenerator(getMutation)
.
Vous pouvez désormais intégrer des mutations DOM dans un flux de contrôle de style synchrone. Que diriez-vous de ça.