Skip to content
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

sveltekit:prefetch not working #2929

Closed
ww9 opened this issue Nov 26, 2021 · 6 comments · Fixed by #2995
Closed

sveltekit:prefetch not working #2929

ww9 opened this issue Nov 26, 2021 · 6 comments · Fixed by #2995
Labels
bug Something isn't working p1-important SvelteKit cannot be used by a large number of people, basic functionality is missing, etc. prefetch router

Comments

@ww9
Copy link

ww9 commented Nov 26, 2021

Describe the bug

When I move mouse pointer over a <a sveltekit:prefetch href="/page2">, no chunks are preloaded.

bug-demo

Reproduction

I created a simple repository to reproduce the issue: https://github.com/ww9/sveltekit-prefetch-test

It was created with npm init svelte@next test and:

  • type: skeleton demo app
  • TypeScript: yes
  • ESLint: yes
  • Prettier: yes

Then added some links with sveltekit:prefetch but it doesn't work.

Logs

No response

System Info

System:
    OS: Windows 10 10.0.19043
    CPU: (12) x64 Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz   
    Memory: 21.07 GB / 31.84 GB
  Binaries:
    Node: 16.6.2 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.17 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 8.1.4 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.19041.1266.0), Chromium (96.0.1054.34)  
    Internet Explorer: 11.0.19041.1202
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.3 
    @sveltejs/adapter-static: ^1.0.0-next.21 => 1.0.0-next.21 
    @sveltejs/kit: next => 1.0.0-next.201 
    svelte: ^3.44.0 => 3.44.2

Severity

annoyance

Additional Information

Svelte is amazing ❤

@Billyzou0741326
Copy link

Billyzou0741326 commented Nov 27, 2021

I can verify sveltekit:prefetch works up to @sveltejs/kit: 1.0.0-next.198. The very next version @sveltejs/kit: 1.0.0-next.199 breaks it. For now I'm pinning my version to 198 :)

Changes between 8727a51 (198) and c7c25c0 (199) has broken the feature

@bluwy bluwy added bug Something isn't working p1-important SvelteKit cannot be used by a large number of people, basic functionality is missing, etc. router labels Nov 28, 2021
@borntofrappe
Copy link
Contributor

borntofrappe commented Dec 4, 2021

I think I found the issue in #2769.

The pull request updates find_anchor starting on line 14 of packages/kit/src/runtime/client/router.js to use event.composedPath()

function find_anchor(event) {
  const node = event
    .composedPath()
    .find((e) => e instanceof Node && e.nodeName.toUpperCase() === "A"); // SVG <a> elements have a lowercase name
  return /** @type {HTMLAnchorElement | SVGAElement | undefined} */ (node);
}

I tried logging event.composedPath() and the method returns an empty array. A stackoverflow question seems to raise the issue when the event is delayed and indeed if we were to remove the delay from the mousemove event the prefetching feature would work.

const handle_mousemove = (event) => {
-  clearTimeout(mousemove_timeout);
-  mousemove_timeout = setTimeout(() => {
    trigger_prefetch(event);
-  }, 20);
};

I'm not suggesting we should, though. Just trying to help and isolate the issue.

The same question suggests saving a reference to the composed path and it'd be possible at the price of updating find_anchor to receive the array instead of the event.

const handle_mousemove = (event) => {
  clearTimeout(mousemove_timeout);
  const composedPath = event.composedPath();
  mousemove_timeout = setTimeout(() => {
    trigger_prefetch(comosedPath);
  }, 20);
};

Let me know if I can help some more :)

Thanks to @Billyzou0741326 for highlighting the change introduced by c7c25c0 (199) 🙌

Update: I created a small demo on CodePen to describe the issue with a trivial example. The mousemove event for the second anchor link element shows the empty array.

@bluwy
Copy link
Member

bluwy commented Dec 5, 2021

Thanks for digging into this @borntofrappe. Looks like this one's tricky to handle. Calling composedPath() in a mousemove event would likely have a performance impact, I wonder if we can create a custom event just before calling trigger_prefetch so the composedPath() works. Or if you have some ideas of a performant solution without regressing #2769.

@borntofrappe
Copy link
Contributor

Early apology for my inexperience with custom events @bluwy

I updated the pen and by creating a custom event I was able to access the array even with a delay.

I tried the same approach in the router as follows:

const handle_mousemove = (event) => {
  clearTimeout(mousemove_timeout);
  mousemove_timeout = setTimeout(() => {
    dispatchEvent(new CustomEvent('trigger'))
  }, 20);
};

dispatchEvent('trigger', trigger_prefetch);

But in this instance the event refers to the window object. e.composedPath() returns an empty array again, just for a different reason.

I got it working listening to the event on the target of the event itself, but I'm not confident in the solution.

const handle_mousemove = (event) => {
  clearTimeout(mousemove_timeout);
  mousemove_timeout = setTimeout(() => {
    event.target.addEventListener('trigger', trigger_prefetch);
    event.target.dispatchEvent(new CustomEvent('trigger'));
    event.target.removeEventListener('trigger', trigger_prefetch);
  }, 20);
};

I hope you'll find a better way 🤞

@bluwy
Copy link
Member

bluwy commented Dec 6, 2021

No worries @borntofrappe. Looks like you're on the right track, we would need to call dispatchEvent from event.target so that composedPath() starts from that element onwards. So your second snippet should work. I also think the addEventListener can be done outside of handle_mousemove since the event bubbles to the window.

With all these said, looks like you've already implemented exactly that in your updated codepen 😄 Feel free to send a PR to fix this.

@borntofrappe
Copy link
Contributor

borntofrappe commented Dec 6, 2021

Understood @bluwy

In the end it's almost frustrating it only takes two lines (took me a while to figure it out).

const handle_mousemove = (event) => {
  clearTimeout(mousemove_timeout);
  mousemove_timeout = setTimeout(() => {
-    trigger_prefetch(event);
+   event.target.dispatchEvent(new CustomEvent('trigger_prefetch', { bubbles: true }))
  }, 20);
};

addEventListener('touchstart', trigger_prefetch);
addEventListener('mousemove', handle_mousemove);
+ addEventListener('trigger_prefetch', trigger_prefetch);

borntofrappe added a commit to borntofrappe/kit that referenced this issue Dec 6, 2021
benmccann pushed a commit to borntofrappe/kit that referenced this issue Dec 12, 2021
Rich-Harris pushed a commit that referenced this issue Dec 22, 2021
* dispatch custom event - fixes #2929

* changeset

* missing semicolon

* clarify pull request

* pnpm check

* update typescript check

* Update packages/kit/src/runtime/client/router.js

Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>

Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working p1-important SvelteKit cannot be used by a large number of people, basic functionality is missing, etc. prefetch router
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants