kit icon indicating copy to clipboard operation
kit copied to clipboard

Add a way to manually restore focus and scroll to hash element

Open f-elix opened this issue 3 years ago • 3 comments

Describe the problem

Sveltekit's router handles very well the focus and hash navigation, but it would be nice to be able to handle this manually sometimes using the same logic as the router.

Use case:

We have a page transition system at the agency where I work that delays the <slot> rendering in +layout.svelte until after the transition animation finishes (on outroend). All the router's focus and hash related code has already run by then, so we have to replicate that logic ourselves and run it after this animation to keep the navigation functioning properly.

Describe the proposed solution

Expose this logic in functions (or a single function that does both) that we can import (I don't really have any great ideas for naming right now).

import { restoreFocus, restoreHash } from '$app/navigation';

Alternatives considered

Replicate this logic in our own code.

Importance

would make my life easier

Additional Information

No response

f-elix avatar Nov 21 '22 21:11 f-elix

I'm not sure I fully understand the transition/animation logic yet. Is it that something from the previous page is still transitioning out while the focus logic already runs, or is it that something is transitioning in while the focus logic already runs, or both?

Not sure if this is possible, but I'm wondering if something like this should be solved at the framework level (Svelte 4+) where Svelte core notifies SvelteKit that things are now done animating, so you wouldn't need to do this manually.

dummdidumm avatar Nov 22 '22 08:11 dummdidumm

I'm not sure I fully understand the transition/animation logic yet. Is it that something from the previous page is still transitioning out while the focus logic already runs, or is it that something is transitioning in while the focus logic already runs, or both?

Not exactly. It's that there is nothing in the DOM when the focus logic runs.

I'll try to explain it briefly.

Our typical +layout.svelte (simplified) looks like this:

<Header />
{#if !$transitioning}
  <main out:fly in:fly on:outroend={endTransition}>
    <slot />
  </main>
{/if}
<Footer />

transitioning is a custom store that we set to true in beforeNavigate. We also cancel the navigation. The out transition plays, then on outroend (in the endTransition function) we call goto to load the next page*. Once the goto promise resolves, we set transitioning back to false, which renders the <slot /> and plays the in animation.

The problem is that we have to wait for goto to resolve before rendering the <slot /> (otherwise the old page renders), which means that the focus and hash logic has already run by then. We would like to be able to run it again when we render the <slot /> ourselves. Does that make more sense to you?

This is our use case, but I don't think it would be farfetched to presume that there would be others that could use these functions.

*Not relevant for this issue, but for the performance minded people, we do prefetch the next page when we cancel the navigation in beforeNavigate so that the network request isn't delayed by our transitions.

f-elix avatar Nov 22 '22 14:11 f-elix

As a non-animation use case, MathJax rendering will cause the contents of the page to move around and hash navigation to look like it missed after MathJax is done. I've been handling this with

mathjaxPromise.then(() => {
    // If the URL has a hash and the reader hasn't scrolled on their own yet,
    // ensure that the relevant section title is scrolled back into view after the content jump
    if ($page.url.hash && !$hasScrolled)
        document.getElementById($page.url.hash.slice(1))?.scrollIntoView();
});

Not difficult, but a restoreHash function would be simpler and this kind of thing would be missed by the framework trying to solve the problem itself.

stephenlrandall avatar Nov 22 '22 16:11 stephenlrandall