You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[css-view-transitions] Behavior of calling document.startViewTransition synchronously more than once #11292
This demo has two successive calls to document.startViewTransition:
document.querySelectorAll('.item label').forEach(($label)=>{// @note: we listen on click because that happens *before* a change event$label.addEventListener('click',async(e)=>{if(!document.startViewTransition)return;e.preventDefault();const$input=$label.querySelector('input');document.startViewTransition(()=>{$input.checked=!$input.checked;});document.startViewTransition(()=>{document.body.style.padding=`${Math.random()*10}rem`;});});});
When you check this in Chrome, the code from both callbacks get executed as part of the View Transition: the faux <details> opens/closes using an animation + it also moves around because the padding of the <body> was changed.
When you check this in Safari, only the code from the second callback gets executed as part of the View Transition: the faux <details> is immediately opened/closed and only moves around because the padding of the <body> was changed.
As per spec, the second call to document.startViewTransition should skip any active view transition, which led me to believe Safari is doing the right thing here.
Checking things with @noamr, he says it’s racy in the spec:
document.startViewTransition(updateCallback2):
2.1 calls transition1.skipTransition() internally
2.1.1 queue a task to call updateCallback1
2.2 wait till the next rendering steps
2.2.1 capture the old state
2.2.2 queue a task to call updateCallback2
There is no guarantee that 2.2.1 would be after/before 2.1.1. It's a race between the rendering steps and the queued update callback.
I’m not sure if an how we can solve, but I surely would love to see all implementations behave the same by having intstructions for what to do in this case baked into the spec.
The best practice here would be to use updateCallbackDone
Sure, that would fix that demo so that it’s consistent across browsers. But I also would love to see it work interoperably without that fix.
I’m fine with whatever the outcome is (altough I expected Safari’s behavior to occur; but also see that Chrome’s behavior seems interesting). My main concern is interop here.
An important detail here is that update callbacks can also be asynchronous and have multiple steps; we can queue them in such a way that they would start in the order of their respective view transitions, but without awaiting it would be constrained to a subset of those callbacks.
@emilio@jakearchibald@nt1m any takes on this?
I think relying on updateCallbackDone is the right thing to do here, but I'm open to exploring queuing the update callbacks in order received rather than race between the successful one and failed ones
That means a failure to create a visual transition, which can happen due to misconfiguration or device constraints, will not prevent the developer’s UpdateCallback being called, even if it’s known in advance that the transition animations cannot happen.
For example, if the developer calls skipTransition() at the start of the view transition lifecycle, the steps relating to the animated transition, such as creating the view transition tree, will not happen. However, the UpdateCallback will still be called. It’s only the visual transition that’s skipped, not the underlying state change.
It'd be nice to remove the race condition somehow though.
Ah right, I was reading it as Safari was executing them in order. Not calling the update callback at all is definitely wrong and a spec violation. Surprised that we don't have WPTs for this.
It'd be nice to remove the race condition somehow though.
Happy to resolve on something and spec it. It's not complex to fix.
Proposing the following resolution: the update callbacks would be maintained in an internal queue rather than separately on the event loop, and when "calling the update callback" all of them would be invoked.
That means a failure to create a visual transition, which can happen due to misconfiguration or device constraints, will not prevent the developer’s UpdateCallback being called, even if it’s known in advance that the transition animations cannot happen.
For example, if the developer calls skipTransition() at the start of the view transition lifecycle, the steps relating to the animated transition, such as creating the view transition tree, will not happen. However, the UpdateCallback will still be called. It’s only the visual transition that’s skipped, not the underlying state change.
But Safari is executing the first callback (the one that changes .checked) just fine. What is different between Chrome and Safari is that Safari seems to flush the queue of previously registered callbacks before taking the snapshots, whereas Chrome executes all previously registered callbacks as part of the resulting transition.
One could say Chrome is in the wrong here, because it changes the .checked state as part of the View Transition, which goes against “It’s only the visual transition that’s skipped, not the underlying state change.” – Chrome does not skip the visual transition.
When you check this in Safari, only the code from the second callback gets executed as part of the View Transition
Ah, my bad, I interpreted the above incorrectly.
What is different between Chrome and Safari is that Safari seems to flush the queue of previously registered callbacks before taking the snapshots, whereas Chrome executes all previously registered callbacks as part of the resulting transition.
What is different between Chrome and Safari is that Safari seems to flush the queue of previously registered callbacks before taking the snapshots, whereas Chrome executes all previously registered callbacks as part of the resulting transition.
Yes. Sorry, that wasn't clear. The Safari behaviour where it makes the previous transition do all its cancelation behaviours before the new transition does any of its capturing sounds good.
PROPOSED RESOLUTION: When starting a new view transition, have the previous active transition do all its cancelation behaviours before the new transition does any of its capturing.
The CSSWG will automatically accept this resolution one week from now if no objections are raised here. Anyone can add an emoji to this comment to express support. If you do not support this resolution, please add a new comment.
Proposed Resolution: When starting a new view transition, have the previous active transition do all its cancelation behaviours before the new transition does any of its capturing.
RESOLVED: When starting a new view transition, have the previous active transition do all its cancelation behaviours before the new transition does any of its capturing.
Activity
noamr commentedon Nov 28, 2024
The best practice here would be to use
updateCallbackDone:bramus commentedon Nov 28, 2024
Sure, that would fix that demo so that it’s consistent across browsers. But I also would love to see it work interoperably without that fix.
I’m fine with whatever the outcome is (altough I expected Safari’s behavior to occur; but also see that Chrome’s behavior seems interesting). My main concern is interop here.
noamr commentedon Nov 28, 2024
An important detail here is that update callbacks can also be asynchronous and have multiple steps; we can queue them in such a way that they would start in the order of their respective view transitions, but without awaiting it would be constrained to a subset of those callbacks.
noamr commentedon Nov 28, 2024
@emilio @jakearchibald @nt1m any takes on this?
I think relying on
updateCallbackDoneis the right thing to do here, but I'm open to exploring queuing the update callbacks in order received rather than race between the successful one and failed onesjakearchibald commentedon Dec 3, 2024
The Safari implementation is definitely wrong, as it violates the intent that transitions are an enhancement.
It'd be nice to remove the race condition somehow though.
noamr commentedon Dec 3, 2024
Ah right, I was reading it as Safari was executing them in order. Not calling the update callback at all is definitely wrong and a spec violation. Surprised that we don't have WPTs for this.
Happy to resolve on something and spec it. It's not complex to fix.
noamr commentedon Dec 3, 2024
Proposing the following resolution: the update callbacks would be maintained in an internal queue rather than separately on the event loop, and when "calling the update callback" all of them would be invoked.
bramus commentedon Dec 3, 2024
But Safari is executing the first callback (the one that changes
.checked) just fine. What is different between Chrome and Safari is that Safari seems to flush the queue of previously registered callbacks before taking the snapshots, whereas Chrome executes all previously registered callbacks as part of the resulting transition.One could say Chrome is in the wrong here, because it changes the
.checkedstate as part of the View Transition, which goes against “It’s only the visual transition that’s skipped, not the underlying state change.” – Chrome does not skip the visual transition.jakearchibald commentedon Dec 3, 2024
Ah, my bad, I interpreted the above incorrectly.
Yeah, I agree that's nice.
bramus commentedon Dec 3, 2024
The Safari behavior?
jakearchibald commentedon Dec 3, 2024
Yes. Sorry, that wasn't clear. The Safari behaviour where it makes the previous transition do all its cancelation behaviours before the new transition does any of its capturing sounds good.
noamr commentedon Jan 21, 2025
Let's tag this for async resolution? @astearns
bramus commentedon Jan 21, 2025
PROPOSED RESOLUTION: When starting a new view transition, have the previous active transition do all its cancelation behaviours before the new transition does any of its capturing.
3 remaining items
nt1m commentedon Jan 21, 2025
Sounds good
astearns commentedon Feb 4, 2025
The CSSWG will automatically accept this resolution one week from now if no objections are raised here. Anyone can add an emoji to this comment to express support. If you do not support this resolution, please add a new comment.
Proposed Resolution: When starting a new view transition, have the previous active transition do all its cancelation behaviours before the new transition does any of its capturing.
astearns commentedon Feb 10, 2025
RESOLVED: When starting a new view transition, have the previous active transition do all its cancelation behaviours before the new transition does any of its capturing.
[css-view-transitions-1] Always perform all the update callbacks befo…
[css-view-transitions-1] Always perform all the update callbacks befo…