-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: fire an event before the first rendering opportunity after activation #9315
Comments
This problematic. Nothing may be re-rendered when coming out of the bfcache, as an example, if the rendering is still cached. |
hmm interesting point about cached rendering for BFCache.
A possible solution is to fire this event if rendering is still cached, but we force it if there's a cross-document transition. /cc @khushalsagar |
Did you mean "not fire" this event if rendering is still cached?
Funny enough, we ran into this while prototyping. The bug is still being fixed. You can see it in action on chrome canary (<116.0.5791.0) if you enable VT on navigations, load https://foil-persistent-bookcase.glitch.me/page2.html, click navigate then go back/fwd. Because the browser displays the cached rendering of the restored page before its first rAF, you get a flash of the restored page and then an animation. The fix is simply to not display the cached rendering. The reason being that the restored page will start rendering with a tree of view-transition pseudo-elements that show a screenshot of the old page. Authors can then customize the animation when the restored page starts rendering, same as what would happen with an SPA back/fwd nav. There is no standardization around displaying the restored page's cached rendering (to my knowledge). Looks like with this feature we'll have to explicitly say, "the browser shouldn't display any cached rendered output of the restored page, first frame must be the output of the first rendering opportunity after |
It doesn't really. Isn't tab switching quite similar case. Browsers do throttle the background tabs heavily, but when making such tab foreground, something needs to be painted, asap, even if there is slow running JS running in that background tab. |
So do you display the tab before the |
@smaug----, I agree that we don't want this policy in general. Chrome has similar behaviour, if you switch tabs (or go to a page restored from BFCache) we flip to the cached rendering of the new page if available. If the transition to the new page is going to be a direct flip, it makes sense to do it asap. I was hoping to carve out an exception if there is a ViewTransition. In this case it makes sense to keep the last rendered output of the old page onscreen until the restored page draws a frame. Because the restored page is going to start with a snapshot of the old page's contents (wrapped up in view-transition pseudo-elements) and customize the transition instead of a direct flip. So the spec should mandate this behaviour only if there is a ViewTransition. @noamr you're right that if the page chooses to update the DOM in |
... In situations where navigation is explicitly different from tab-switching, i.e. there's a However, delaying is not the problem here. In the case of having cached rendering on BFCache traversal and no view-transition, we can fire that event without re-rendering. This would keep this "cached rendering" thing an implementation detail that's not observable. The event should be somewhat equivalent to |
So if pageshow listener does slow things, like it often may do, rendering might be postponed significantly and user experience would be worse, since user wouldn't see anything. Or I'm not sure what would be visible... the previous page, while the current is already the one which got out of bfcache...that might be a security issue then. |
Not sure how
|
You said |
Right, it would be somewhat equivalent to that in behavior. But not suggesting to change the behavior of |
Let's take this up in a separate issue, filed w3c/csswg-drafts#8888. The desired behaviour is needed irrespective to the event proposed here. |
This was discussed at CSSWG recently, the notes are on w3c/csswg-drafts#8805 (comment). There is general support of the idea but we need a discussion at HTML WG to narrow down the specifics of when the event is dispatched and whether it should be VT specific. |
@noamr's feedback below. Re: whether this event should fire only when there is a transition, one of the use-cases to fire it every time is detecthing whether is a transition to execute code which is deferred until the end of the transition. For example: function hideLoader() { ... }
document.addEventListener("reveal", event => {
if (event.viewTransition) {
event.viewTransition.finished.then(hideLoader);
} else {
hideLoader();
}
}); A transient loading UI which is hydrated with content when the transition is finished. If the event is not fired when there is no transition then authors will have to write awkward code to track whether the event was fired and run |
At the HTML spec triage, we've talked about a possibility of the rAF based solution. Below roughly would be the equivalent required for the same effect (I actually don't know of a sure way to hook into the first rAF, but I think just doing requestAnimationFrame works) <script>
function runRevealEvent() {
if (document.activeViewTransition) {
...
}
...
}
// For the "very first rAF" (this works, right?)
requestAnimationFrame(runRevealEvent);
// For BFCache activation
addEventListener("pageshow", e => { if (e.persisted) { runRevealEvent() } });
</script> With the reveal event, the code would be the following: <script>
// This fires at "the right time" whether or not it's a new navigation
// or BFCache activation
addEventListener("reveal", e => {
if (e.viewTransition) {
...
}
...
});
</script> To make my case: the script here is by no means complicated, but for the user adopting view transitions, in the latter case, the only way to get the viewTransition object is to listen to this event, and this event will work correctly for BFCache and new navigation cases. This seems hard to get wrong. In the former, it seems easy enough to miss the BFCache case. Also, the
/cc @smaug---- @mfreed7 |
One very nice feature of rAF based solution is that it is basically an explicit request to paint. Adding an event listener is not. And when coming out of bfcache one might not paint normally, because it would be just useless if the rendering has been cached. Sure, implementation might optimize behavior so that painting would happen if event listener is there and avoid useless paints without it. And in the first example the question about 'very first rAF...?' applies to adding the event listener too. Tab switching is still something a bit unclear to me. If a page is loaded in a background tab, it might not be painted at all before the tab is brought to foreground. I guess I don't know how view transitions are supposed to work in that case. (And yesterday when I talked about FF not implementing render blocking, I meant explicit blocking="render" . FF bug) |
Why does this need to be an explicit request to paint? The problem with the rAF-based solution is that it's very easy to overlook BFCache-restore, creating bugs. /* in <head> */
function animateTransitionWithWebAnimations() { if (document.inboundViewTransition) { ... } }
requestAnimationFrame(animateTransitionWithWebAnimations);
document.addEventListener("pageshow", animateTransitionWithWebAnimations); It's OK to do this and educate people about it but it seems awkward.
There are no view transitions in tab switching. I think the reveal event should apply if it's the first render opportunity in that tab.
|
The need to have two different ways to hook into rAF (initial frame + pageshow for persisted case) is going to make this approach error prone. I still prefer that we consider the reveal event (perhaps named something less similar to pageshow). I also want to clarify that the rAF based solution isn't "free". That is, it isn't only using existing features: it also requires activeViewTransition (or inboundViewTransition as @noamr called it). So I think the cost between the two solutions is similar. The pro of the rAF based solution is that we don't need to worry about event timing, but the con is that the developer can get the pattern wrong pretty easily (omitting BFCache case for example). The pro of the "reveal" event is that it's hard for the developer to get it wrong. The con is that it may be trickier to specify in all cases (e.g. tab switching). Let me know if you disagree with this assessment. If it's correct, then I think we should prioritize the ease of use for the developer |
The challenge with the reveal event is that when activating from BFCache there might not be an animation frame at all (on Firefox). This is an actual issue. Perhaps we should fire the reveal event:
|
Would (I'm also happy with other proposals that people have put up though) |
|
Ok, yeah I didn't realize that we're already showing a cached frame. I don't know if |
Yea, the updated content is being revealed. |
So in the bfcache case where there is no new render update step, the event is fired anyway (but no animation frame callback etc), correct? Same timing as |
Based on previous comments in this discussion, It would fire at the next render after activation, whenever that happens. |
Then I think |
FWIW I don't think its very expensive to force a "update the rendering" loop after activation to ensure this event is fired after BFCache restore, if that's what authors would expect. Kinda like how registering a raf handler forces a frame. We'll hit an early out fairly quickly, it won't be an expensive operation. So we should do what authors would expect. @vmpstr made a good point that someone might use this event to register a raf handler, and be surprised that the event never ran after BFCache restore unless something else was damaged. |
@khushalsagar Yes, it might be better to always fire it after (In spec terms, this is whether we put this event before or after the early exit for "nothing to do"). If we do this, do you still think |
(except in the first-load case where |
Yes, I meant the reactivation |
I lean towards "firstrender" over "firstframe", frame seems ambiguous from the spec perspective. "firstrender" is a shorter version of "firstrenderingopportunity" in my mind. I like the idea of using "first" in the name, conceptually this is the first frame after some specific events in the Document's lifecycle: first load, pre-render activation, BFCache activation. I realize its not obvious what those events are from the term "first" but I can't think of anything more terse. We could go with "firstrenderonactivation". It sounds like activation is a well understood term in the navigation lifecycle to mean that the Document is being presented to the user (as opposed to being in the background). |
I like
Note that though activation is well understood in the spec, it's not web exposed in APIs so much. |
Wait... I thought I tried to make it clear... Similarly for |
How about we change the behavior slightly, to also depend on visibility, and call it This would perhaps clarify the purpose of this event - it's the first time where you can update the rendering and have it be presented instantly. If there are reasons against this behavior change, I would suggest to move back to |
Either |
|
Given the behaviour is:
Based on that, |
I'm fine with |
See arguments in whatwg/html#9315 Bug: 1466250 Change-Id: I4e722d3d71a707352054f9408ab01fb54dbf2954 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4902322 Commit-Queue: David Bokan <bokan@chromium.org> Reviewed-by: Khushal Sagar <khushalsagar@chromium.org> Cr-Commit-Position: refs/heads/main@{#1203002}
The pagereveal event is fired at the beginning of the first rendering opportunity after activation (initial load or reactivation). It is a way for the author to execute some JS that affects the presentation "just in time" for the first frame. If there is an inbound cross-document view transition, the reveal event holds a reference to the ViewTransition object. Closes #9315.
Add pagereveal event The pagereveal event is fired at the beginning of the first rendering opportunity after activation (initial load or reactivation). It is a way for the author to execute some JS that affects the presentation "just in time" for the first frame. If there is an inbound cross-document view transition, the reveal event holds a reference to the ViewTransition object. Closes whatwg#9315. Use UA styles rather than prose to define <input> clip The previous prose to make `overflow` act as `visible` with regards to other CSS features but still clip didn't work well with e.g. `text-overflow: ellipsis`. CSS now has a standard way to do what `input` buttons need, i.e. clip and also not affect interaction with `vertical-align`. Fixes whatwg#9976. Forbid nesting <details> in the same exclusive accordion Fixes whatwg#9968.
The problem: we have several lifecycle/visibility events, like
pageshow
, but none of them take progressive rendering into account. There is currently no event that is fired before the first rendering opportunity after activation (either due to a new document or BFCache/prerender reactivation).This is needed for cross-document view transitions, as well as for metrics, and solving this can allow the developer to perform "last minute" DOM changes after the document is initialized but before it's rendered.
The proposal: Expose an event (
reveal
?beforepageshow
?) that is guaranteed to be called before the firstrequestAnimationFrame
callback but after activation and after a document is no longer render-blocked.When a cross-document view transition is present, this event would include a reference to the ViewTransition object, which would allow the new document to observe when the transition is finished, skip it, or potentially extend it. See explainer.
The text was updated successfully, but these errors were encountered: