sveltejs / kit

web development, streamlined
https://kit.svelte.dev
MIT License
18.4k stars 1.88k forks source link

Custom Element (created by js) doesnt load correctly if pageoption ssr=true and csr=true but if ssr != csr #12032

Open Raphael2b3 opened 5 months ago

Raphael2b3 commented 5 months ago

Describe the bug

When using CustomElements created by js u cant use them in +page.svelte or +layout.svelte file if the pageoption is ssr = true and csr=true the default.

The CustomElement loads first correctly during ssr but after csr has finished it disappears.

How ever when settings ssr=false so that csr=true. It loads correctly.

So customelements work for ssr only and for csr only but not when both is activated.

See the video for demonstration.

https://github.com/sveltejs/kit/assets/70175424/c35204af-ddd8-4b66-b76f-34bbd7649ef0

Reproduction

create the sveltekit project with vite

npm create vite@latest  // sveltekit with ts

in the static folder add custom-element.js:

class CustomElement extends HTMLElement {
  connectedCallback() {
    this.classList.add("test");
    this.innerHTML = `
     <h1>Hello, Custom element!</h1>
    `;
  }
}
customElements.define("custom-element", CustomElement);

console.log("Custom element is defined");

replace the content of app.html with:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%sveltekit.assets%/favicon.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    %sveltekit.head%
    <script type="module" src="/custom-element.js"></script>
  </head>
  <body data-sveltekit-preload-data="hover">
    <custom-element></custom-element>

    <div style="display: contents">%sveltekit.body%</div>
  </body>
</html>

replace the content of +page.svelte at the root of your project with:

<custom-element></custom-element>

add a file called +page.ts:

import type { PageLoad } from "./$types";

export const load = (async () => {
  return {};
}) satisfies PageLoad;

// this example only works if ssr != csr, the default value is true for both, 
// to make it work comment in one of the statements

//export const ssr = false;
//export const csr = false;

Alternatively unpack this zip archive and run npm i and npm run dev example.zip

Logs

Custom element is defined custom-element.js:11:9
[vite] connecting... client.ts:19:8
[vite] connected. client.ts:173:14
Source-Map-Fehler: No sources are declared in this source map.
Ressourcen-Adresse: http://localhost:5173/node_modules/.vite/deps/chunk-F3FYYIAV.js?v=727b62fe
Source-Map-Adresse: chunk-F3FYYIAV.js.map

Source-Map-Fehler: No sources are declared in this source map.
Ressourcen-Adresse: http://localhost:5173/node_modules/.vite/deps/svelte.js?v=727b62fe
Source-Map-Adresse: svelte.js.map

Source-Map-Fehler: No sources are declared in this source map.
Ressourcen-Adresse: http://localhost:5173/node_modules/.vite/deps/svelte_store.js?v=727b62fe
Source-Map-Adresse: svelte_store.js.map

Source-Map-Fehler: No sources are declared in this source map.
Ressourcen-Adresse: http://localhost:5173/node_modules/.vite/deps/svelte_internal.js?v=727b62fe
Source-Map-Adresse: svelte_internal.js.map

System Info

System:
    OS: Windows 10 10.0.19045
    CPU: (8) x64 Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
    Memory: 6.05 GB / 15.87 GB
  Binaries:
    Node: 20.9.0 - C:\Program Files\nodejs\node.EXE
    npm: 10.5.0 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Chromium (122.0.2365.92)
    Internet Explorer: 11.0.19041.3636
  npmPackages:
    @sveltejs/adapter-auto: ^3.0.0 => 3.1.1
    @sveltejs/kit: ^2.0.0 => 2.5.4
    @sveltejs/vite-plugin-svelte: ^3.0.0 => 3.0.2
    svelte: ^4.2.7 => 4.2.12
    vite: ^5.0.3 => 5.2.5

Severity

annoyance

Additional Information

No response

Raphael2b3 commented 5 months ago

an workaround is to load the Custom-Element in a svelte component onMount. +layout.svelte seems to be a good place.

+layout.svelte

<script>
    import { browser } from "$app/environment";

    if (browser) {
        class CustomElement extends HTMLElement {
            connectedCallback() {
                this.classList.add("test");
                this.innerHTML = `<h1>Hello, Custom element!</h1>`;
            }
        }
        customElements.define("custom-element", CustomElement);

        console.log("Custom element is defined");
    }
</script>

<custom-element></custom-element>

AND

Now the problem is that custom element is only defined on csr and the website will not be shown if js disabled or until the csr has finished

So we need to include the script at the app.html file.

app.html


<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <link rel="icon" href="%sveltekit.assets%/favicon.png" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        %sveltekit.head%
        <script type="module">
                      class CustomElement extends HTMLElement {
                        connectedCallback() {
                          this.classList.add("test");
                          this.innerHTML = `
                           <h1>Hello, Custom element!</h1>
                          `;
                        }
                      }
                      customElements.define("custom-element", CustomElement);

                      console.log("Custom element is defined");

                </script>
    </head>
    <body data-sveltekit-preload-data="hover">
        <custom-element></custom-element>
        <div style="display: contents">%sveltekit.body%</div>
    </body>
</html>

Its kinda frustrating that its not enaugh to just load the custom-element in app.html but this will do the job aswell

Rishab49 commented 5 months ago

have you tried {@html "<custom-element></custom-element>"}? It is working for me

Raphael2b3 commented 5 months ago

This is not an Option for me, since i lose reactivity to state when i want custom-element to depend on state