Csswg-drafts: [css-transitions] why doesn't transitionend event fire in some cases?

Created on 22 Aug 2018  路  9Comments  路  Source: w3c/csswg-drafts

In the spec for transitionend event, it mentions that the transitionend event does not fire

in the case where a transition is removed before completion, such as if the transition-property is removed

I take it that it also does not fire in cases where the element is removed from the DOM, like if set to display:none, etc. Was this decision intentional? why does it not fire in these cases?

I have started a wait-for-element-transition library because current transitionend is not always reliable when you want to just write one set of code for both animated and non-animated set of elements or in the case where whether a set of elements can be animated is unknown. My lib allows a consumer to wait for an element to transition by returning a promise that resolves. The promise will resolve immediately if element is removed from DOM (or if display:none is applied) after the transition has started.

I would rather not have to use a library for this so just wondering if there is a possibility to update the spec to support this use case.

Thanks for your time!

Most helpful comment

Yes, that's correct, but getAnimations() won't be shipping in any browser for some time yet.

Until that ships, there's no way to detect currently running transitions. You have to detect them as they are triggered using transitionrun. I suspect that for your library it depends if you expect callers to call waitForElementTransition before or after transitions are generated.

In the sample code there, it is being called _before_ transitions are generated (since classList.add() doesn't trigger a style flush). In that case you'd wait for the next animation frame, depending on where in the event loop waitForElementTransition is called possibly wait another animation frame so that style is updated, then if there there's no transitionrun event fired for the element, resolve the Promise at that point. Alternatively, you could flush style inside waitForElementTransition and then spin the event loop to check for a transitionrun event.

If you want callers to be able to call waitForElementTransition _after_ transitions are generated then you'll probably need to do global bookkeeping (listening to transitionrun, transitionend, and transitioncancel events on the root element and tracking which elements have running transitions there).

(For what it's worth, using timeouts to deal with transitionend events not firing is something we used to do in Firefox OS and became the motivating force for adding transitioncancel and transitionrun to the spec.)

All 9 comments

Have you tried the transitioncancel event?

I've seen it but I didn't think the "canceled" event was relevant. The spec doesn't go into anymore detail other than to say it fires when a transition is "cancelled", which I assumed only meant when cancel() method is called on an animation. But not sure what that means. Does removing the transition-property from an element or changing to display:none cause transitioncancel to fire?

The spec says https://drafts.csswg.org/css-transitions/#ref-for-transition-cancel

If an element is no longer in the document, implementations must cancel any running transitions on it

https://drafts.csswg.org/css-transitions/#ref-for-transition-cancel%E2%91%A0

If the element has a running transition or completed transition for the property, and there is not a matching transition-property value, then implementations must cancel the running transition

Ok thanks for that information. I missed the text in that first link you posted. So looks like transitioncancel could work when element css is changed (i.e. to display:none or if transition properties are removed from element's css) after a transition has begun or will eventually begin. Great!

But how would I wait for a group of elements to transition and some of them dont have transition properties on them at all? In my library, I resolve the "waiting promise" immediately. So I guess I would still need my library to simplify interoperability of animatable vs non-animatable elements in these cases?

You'd listen for transitionrun events. If you got a transitionrun event you will either get a transitionend or transitioncancel.

(Once we ship getAnimations() you'll also just be able to wait on Promise.all(elem.getAnimations().map(animation => animation.finished)) since the finished promise rejects if the animation is canceled.)

I see. So elem.getAnimations() may be what I'm looking for to detect when an element has no animations. I am reading the spec on the getAnimations() but can't find anywhere that describes what should happen when an elem has no animations to get. Would I be able to do something like getAnimations().length === 0? which would mean no animations are present? and I can resolve the wait promise immediately in my library?

Yes, that's correct, but getAnimations() won't be shipping in any browser for some time yet.

Until that ships, there's no way to detect currently running transitions. You have to detect them as they are triggered using transitionrun. I suspect that for your library it depends if you expect callers to call waitForElementTransition before or after transitions are generated.

In the sample code there, it is being called _before_ transitions are generated (since classList.add() doesn't trigger a style flush). In that case you'd wait for the next animation frame, depending on where in the event loop waitForElementTransition is called possibly wait another animation frame so that style is updated, then if there there's no transitionrun event fired for the element, resolve the Promise at that point. Alternatively, you could flush style inside waitForElementTransition and then spin the event loop to check for a transitionrun event.

If you want callers to be able to call waitForElementTransition _after_ transitions are generated then you'll probably need to do global bookkeeping (listening to transitionrun, transitionend, and transitioncancel events on the root element and tracking which elements have running transitions there).

(For what it's worth, using timeouts to deal with transitionend events not firing is something we used to do in Firefox OS and became the motivating force for adding transitioncancel and transitionrun to the spec.)

Awesome. This is great information @birtles. I will optimize waitForTransition and use that until getAnimations() is finalized. I will close this issue now since my questions have been answered. Thanks!

Ok thanks for that information. I missed the text in that first link you posted. So looks like transitioncancel could work when element css is changed (i.e. to display:none or if transition properties are removed from element's css) after a transition has begun or will eventually begin. Great!

But how would I wait for a group of elements to transition and some of them dont have transition properties on them at all? In my library, I resolve the "waiting promise" immediately. So I guess I would still need my library to simplify interoperability of animatable vs non-animatable elements in these cases?

This issue discussion has just saved my day

Was this page helpful?
0 / 5 - 0 ratings