Skip to content

[css-view-transitions-2] Declaring when an MPA transition can start #8681

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

Closed
jakearchibald opened this issue Apr 4, 2023 · 6 comments
Closed
Labels
css-view-transitions-2 View Transitions; New feature requests

Comments

@jakearchibald
Copy link
Contributor

I don't think MPA transitions should delay the swap from the old to new document by default. Instead, the transition should happen on first render. This means, when the transition starts, the page may not have fully parsed, and things like images may not have loaded.

With SPA, you can preload assets before calling startViewTransition. With MPA, you can use prerender, but you don't get feedback when the prerender is 'ready'.

Blocking attributes can be used by the incoming page to delay the document swap. However, these attributes currently only apply to script/stylesheets. We could expand the set of elements they apply to, such as images and preload. We could add a special blocking value "view-transition", so the blocking only applies if a view transition is being attempted.

We could also allow first render to be delayed by JavaScript:

document.addEventListener('crossdocviewtransition', (event) => {
  event.waitUntil(promise);
});

Although we'd want to avoid infinite delays, like we already do with startViewTransition.

@jakearchibald jakearchibald added the css-view-transitions-2 View Transitions; New feature requests label Apr 4, 2023
@noamr
Copy link
Collaborator

noamr commented May 25, 2023

Yea I think we should have this, though without the event:

document.activeTransition?.waitUntil(promise)

@jakearchibald
Copy link
Contributor Author

I think the problem with that is it's unclear when calling waitUntil would work. With the event the rules are simple: If you get the event, you can call waitUntil in the same task.

With your proposal, it's a value that the UA is going to set and unset at some points, and without an event, there's no indication when it'll be set and unset. Also, when is it unset? Presumably when the transition stops being 'active', which means transition.finished. That means there'll be a period where document.activeTransition exists, but document.activeTransition.waitUntil() will fail, because the transition has already started.

There's also an issue with naming. Would document.startViewTransition cause a new document.activeTransition to be set? If so, what happens with document.activeTransition.waitUntil, since that API doesn't make sense on same-document transitions.

If the event is useful for other things, it might be worth using the event for this too, as it doesn't have the above issues.

@noamr
Copy link
Collaborator

noamr commented May 25, 2023

I think the problem with that is it's unclear when calling waitUntil would work. With the event the rules are simple: If you get the event, you can call waitUntil in the same task.

With your proposal, it's a value that the UA is going to set and unset at some points, and without an event, there's no indication when it'll be set and unset.

It will be set at document initialization and stay there until rendering is unblocked.
In that sense, there's no difference between calling activeTransition?.waitUntil(...) and addEventListener("crossdocviewtransition", e => e.waitUntil...). You have to add the event listener in the head, so might as well waitUntil in the head. The same question would apply to "how do you know to add the event listener? Maybe it's already been called" since it's fired very early.

Also, when is it unset? Presumably when the transition stops being 'active', which means transition.finished. That means there'll be a period where document.activeTransition exists, but document.activeTransition.waitUntil() will fail, because the transition has already started.

There's also an issue with naming. Would document.startViewTransition cause a new document.activeTransition to be set? If so, what happens with document.activeTransition.waitUntil, since that API doesn't make sense on same-document transitions.

Also domUpdated doesn't exactly make sense in the MPA case.
Perhaps ViewTransition could have a subclass/superclass with slightly different properties for MPA vs SPA, because of this?
Note that this problem also exists in the event if you want to be able to skip transition. (event.transition would not exactly be a ViewTransition)

We can perhaps call it pendingTransition, and it's there only in head scripts or in the pageshow event.

If the event is useful for other things, it might be worth using the event for this too, as it doesn't have the above issues.

It's useful but it has some gotchas, like what happens if rendering is cached so there's no rendering coming at all. I'm still pursuing it though.

One thing I'm slightly concerned about with this event is that people would implement it with first-navigation in mind, and then create infinite-wait bugs for reactivate. Let's take the use case of blocking the transition until the hero image has loaded:

e.g.:

<head>
  <script>
  addEventListener('crossdocviewtransition', e => e.waitUntil(new Promise(resolve =>
    document.addEventListener("load", loadEvent => {
      if (loadEvent.target.id === 'hero')
        resolve();
    }, {capture: true})));
  </script>
</head>

Of course it's simple to fix this, but also easy to overlook this - it would work great when you run it locally, but then when you have BFCache/prerender it would wait forever.

What I'm getting at is, I'm not sure we should have an event that abstract away the difference between reactivation and initial render with an event that fires in both. You have an event for the former and the latter is a synchronous script in head, and in many cases there would be subtle differences anyway, so might as well deal with them directly rather than pretend that they're the same case.

@jakearchibald
Copy link
Contributor Author

jakearchibald commented May 26, 2023

It will be set at document initialization and stay there until rendering is unblocked.

That seems reasonable. I was confused by the naming initially, since 'active' has a wider scope. I think there's a more general feature here around blocking rendering, so I'd advise splitting this into two parts:

  • A way to control render blocking
  • A way to know there's a pending cross-document transition (in case render blocking should be different in that case)

However, it's worth considering if there are cases where the developer would want to control render blocking when the page is coming out of bfcache.

Also domUpdated doesn't exactly make sense in the MPA case.
Perhaps ViewTransition could have a subclass/superclass with slightly different properties for MPA vs SPA, because of this?

This is discussed in #8682. I really recommend catching up with the current state of the design. A lot of this stuff has already been discussed internally and with CSSWG folks like Tab. I'm not saying that the design is perfect, or even good, but I think there's value in at least reading it.

One thing I'm slightly concerned about with this event is that people would implement it with first-navigation in mind, and then create infinite-wait bugs for reactivate. Let's take the use case of blocking the transition until the hero image has loaded:

e.g.:

<head>
  <script>
  addEventListener('crossdocviewtransition', e => e.waitUntil(new Promise(resolve =>
    document.addEventListener("load", loadEvent => {
      if (loadEvent.target.id === 'hero')
        resolve();
    }, {capture: true})));
  </script>
</head>

Sighhhhh we really should have promises for this stuff.

What I'm getting at is, I'm not sure we should have an event that abstract away the difference between reactivation and initial render with an event that fires in both.

But you also don't want transitions that fail or look faulty on certain kinds of traversal, because the developer has to add extra code for bfcache transitions, and they didn't test that bit.

That said, I agree that in regards to render blocking, what you'd do for initial load is different to coming out of bfcache. I think the best way to approach is to look at render blocking as a more general feature.

@noamr
Copy link
Collaborator

noamr commented May 26, 2023

It will be set at document initialization and stay there until rendering is unblocked.

That seems reasonable. I was confused by the naming initially, since 'active' has a wider scope. I think there's a more general feature here around blocking rendering, so I'd advise splitting this into two parts:

  • A way to control render blocking
  • A way to know there's a pending cross-document transition (in case render blocking should be different in that case)

However, it's worth considering if there are cases where the developer would want to control render blocking when the page is coming out of bfcache.

Also domUpdated doesn't exactly make sense in the MPA case.
Perhaps ViewTransition could have a subclass/superclass with slightly different properties for MPA vs SPA, because of this?

This is discussed in #8682. I really recommend catching up with the current state of the design. A lot of this stuff has already been discussed internally and with CSSWG folks like Tab. I'm not saying that the design is perfect, or even good, but I think there's value in at least reading it.

Yea I went through all of it but it's spread over a couple of dozens of issues so I'm sure I missed some points. Will catch up on that one.

One thing I'm slightly concerned about with this event is that people would implement it with first-navigation in mind, and then create infinite-wait bugs for reactivate. Let's take the use case of blocking the transition until the hero image has loaded:
e.g.:

<head>
  <script>
  addEventListener('crossdocviewtransition', e => e.waitUntil(new Promise(resolve =>
    document.addEventListener("load", loadEvent => {
      if (loadEvent.target.id === 'hero')
        resolve();
    }, {capture: true})));
  </script>
</head>

Sighhhhh we really should have promises for this stuff.

What I'm getting at is, I'm not sure we should have an event that abstract away the difference between reactivation and initial render with an event that fires in both.

But you also don't want transitions that fail or look faulty on certain kinds of traversal, because the developer has to add extra code for bfcache transitions, and they didn't test that bit.

That said, I agree that in regards to render blocking, what you'd do for initial load is different to coming out of bfcache. I think the best way to approach is to look at render blocking as a more general feature.

Right, this is exactly where I'm at... Before we get to a general-purpose potential-footgun, let's try to fix this more from the use case site and see what the need is afterwards.

@noamr
Copy link
Collaborator

noamr commented Oct 26, 2023

This is handled in the spec draft.

@noamr noamr closed this as completed Oct 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-view-transitions-2 View Transitions; New feature requests
Projects
None yet
Development

No branches or pull requests

2 participants