Race condition when toggling classes disabling CSS transitions

RESOLVED INVALID

Status

()

RESOLVED INVALID
6 years ago
6 years ago

People

(Reporter: mgol, Unassigned)

Tracking

21 Branch
x86_64
Mac OS X
Points:
---

Firefox Tracking Flags

(Not tracked)

Details

Go to:
http://jsfiddle.net/m_gol/3hwjC/19/
and try clicking in the result pane. The box should be animated when going to top and bottom, but it's transitioned only in the second case. I see this behavior in Firefox, Opera and IE10, WebKit browsers behave differently (though IE & Opera don't experience the second race condition behavior I'm describing below, please keep reading).

Because I want to transition the box moving upwards and it doesn't initially have 'top' set in CSS, I need to set it to window height minus box height while transitions are disabled. I am using a special class disabling transitions to set the initial value.

However, even though I am removing the class later, transitions are still disabled when setting top to 50px; If I wrap this assignment in a setTimeout, even with timeout equal to 0:
http://jsfiddle.net/m_gol/3hwjC/21/
Opera and IE10 start to transition properly, however Firefox doesn't. Setting timeout to 50ms works, setting it to e.g. 10ms doesn't. I suspect the number can depend on many different things so there's obviously a race condition happening here.

I've confirmed the issue in Fx stable (18) and nightly (21).
Per spec, whether a transition is started or not depends on the values of various CSS properties when the computed style changes.

What the spec doesn't define is _when_ computed styles change; that's implementation-dependent.

In this case, I would expect computed styles to change once when the window.innerHeight call is done and then change again all at once (so doing the div.style.top, div.className, and second div.style.top set) sometime later when style is recomputed.  At that point we see "top" going from "auto" to "50px", and don't transition.

So whatever you're trying to do with the two separate style.top sets isn't guaranteed to work per spec; the UA just sees the later style.top set in many cases. In Opera and IE it sounds like just setting a timeout is enough to get them to process the first style set (though note that when they do the "no-transitions" class is NOT set on the element), but Firefox does async restyle processing off the refresh timer, so once about 16ms or so.  Hence a short timeout can easily not lead to restyle processing.

If you really want to force a change from (innerHeight-50) to 50 you need to make sure style computation happens in between (for example by querying the computed "top" value on the div at the point when you want to force computation; in your case presumably right before you set className to '').
Status: UNCONFIRMED → RESOLVED
Last Resolved: 6 years ago
Resolution: --- → INVALID
Thanks for your help! However, I wonder if there's any more pretty way to handle the case. If one wants to perform a CSS transition between two states which isn't immediately seen in CSS (sth. like moving from height: auto to 0 or the thing I demonstrated here: moving from 'bottom: sth' to 'top: sth') I think my attempt seems quite natural. One then stumble to various problems caused by under-the-hood optimizations performed by browsers, causing sth I've heard to be called a leaky abstraction.

It seems there should be a simpler way to do it; but if there's not at the moment, this is probably more a meta-discussion about the spec than a bug report. Anyway, I just wanted to put it as a problem to potentially consider.

Thanks again for your help!
> It seems there should be a simpler way to do it

Yes, the way transitions start is widely acknowledged to be a disaster.  People are trying to figure out better ways to do it...

That said, in this particular case you can work around it by simply not using auto top, right?
Yeah, the problem is the box I transition is a footer glued to the bottom of the page which reveals content filling most of the screen on click so if I used top in CSS I'd need to correct it on window resize. I think I prefer to use your advice and access style.top after changes I need to enforce (I've just checked and it works fine everywhere).
> so if I used top in CSS I'd need to correct it on window resize

  top: calc(100% - 50px);
I wish! But Opera doesn't support calc() and it has almost 7% usage in Poland which is a target market for this site.

Besides, Safari 6 has extremely buggy calc support (it's ok in WebKit nightlies so next Safari should be fine when it's out); if you set height using calc() in CSS and then change element height or even width (!) using JS APIs, calc() on height gets reset...

If you're in position to ping Opera devs about this, I'd be grateful. :)
Hmm.  So I was figuring if you set "top" to calc() like that then it would work fine in UAs that support it, and in other UAs you could keep the existing hack... but you wouldn't have to worry about making that hack work in UAs that support calc().  Since the hack is just a no-op when it doesn't work, it should work out.

That's assuming that calc() implementations are not too buggy, which should hopefully be true for unprefixed calc().
I need to force style refresh in a few other case like I described in my report here with IE10 & Opera and in Opera I can't use calc(). It's easier just to keep an existing hack for now, especially that WebKit behaves quite differently than other browsers here (tldr: it's applying transitions almost everywhere in my testcase, they must be caching className changes together with style changes) so I have to workaround their implementation anyway. My report about this:
https://bugs.webkit.org/show_bug.cgi?id=107288
You need to log in before you can comment on or make changes to this bug.