Chainable one-way events with promises
In case that you need one-way events just like JQuery's one()
, I found this pattern handy:
function awaitTransitionEnd(transitionProperty, el, triggerFunction) {
return new Promise((resolve, reject) => {
const handler = (e) => {
if (e.propertyName !== transitionProperty) {
return;
}
el.removeEventListener('transitionend', handler);
resolve(e);
}
el.addEventListener('transitionend', handler);
triggerFunction(el);
});
}
You can then chain CSS transitions like in this example:
awaitTransitionEnd(
'background-color', myEl, () => myEl.classList.replace('bg-red', 'bg-green')
).then(() => awaitTransitionEnd(
'opacity', myEl, () => myEl.classList.add('opacity-0')
)).then(() => awaitTransitionEnd(
'opacity', myEl, () => myEl.classList.remove('opacity-0')
));
If you don't want to use arrow functions, you must pass the event + element like so:
awaitTransitionEnd('background-color', myEl, function(el) {
el.classList.replace('bg-red', 'bg-green');
}).then(function(e) {
return awaitTransitionEnd('opacity', e.target, function(el) {
el.classList.add('opacity-0');
});
}).then(function(e) {
return awaitTransitionEnd('opacity', e.target, function(el) {
el.classList.remove('opacity-0');
});
});
When awaitTransitionEnd
is a class method and you don't want to use arrow functions, you must bind this
to each then()
-closure:
//[...]
.then(function(e) {
return this.awaitTransitionEnd('opacity', e.target, function(el) {
el.classList.add('opacity-0');
});
}.bind(this)).then(//[...]