ETA 24 Apr 17
Ich wollte dies mit etwas async
/ await
Magie ein wenig vereinfachen , da es dadurch viel prägnanter wird:
Unter Verwendung der gleichen versprochenen beobachtbaren:
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);
})
}
Ihre aufrufende Funktion kann so einfach sein wie:
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)
}
}
Wenn Sie eine Zeitüberschreitung hinzufügen möchten, können Sie ein einfaches Promise.race
Muster verwenden, wie hier gezeigt :
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
Sie können dies ohne Bibliotheken tun, müssen jedoch einige ES6-Elemente verwenden. Beachten Sie daher die Kompatibilitätsprobleme (dh, wenn Ihre Zielgruppe hauptsächlich aus Amish-, Luddite- oder, schlimmer noch, IE8-Benutzern besteht).
Zuerst verwenden wir die MutationObserver-API , um ein Beobachterobjekt zu erstellen. Wir werden dieses Objekt in ein Versprechen einwickeln, und resolve()
wenn der Rückruf ausgelöst wird (h / t davidwalshblog), wird David Walsh Blog-Artikel über Mutationen veröffentlicht :
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);
})
}
Dann erstellen wir eine generator function
. Wenn Sie diese noch nicht verwendet haben, verpassen Sie sie - aber eine kurze Zusammenfassung lautet: Sie läuft wie eine Synchronisierungsfunktion, und wenn sie einen yield <Promise>
Ausdruck findet, wartet sie nicht blockierend auf das Versprechen erfüllt ( Generatoren leisten mehr als das, aber das interessiert uns hier ).
// 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")
}
Ein schwieriger Teil bei Generatoren ist, dass sie nicht wie eine normale Funktion "zurückkehren". Wir werden also eine Hilfsfunktion verwenden, um den Generator wie eine reguläre Funktion verwenden zu können. (wieder h / t zu 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 );
}
}
})();
}
Führen Sie dann zu jedem Zeitpunkt, bevor die erwartete DOM-Mutation auftreten könnte, einfach aus runGenerator(getMutation)
.
Jetzt können Sie DOM-Mutationen in einen synchronen Kontrollfluss integrieren. Wie wäre es damit?