How to check when window becomes available?

How to check when window becomes available?
javascript
Ethan Jackson

I am new to SvelteKit and I am not familiar with SSR. I have recently learned that window is only defined after the client-side has been loaded, which can be determined with the onMount hook.

But here's something that I am not clear on. Let's say I have the following component:

<script lang="ts"> const getRem = (rem: number) => rem * parseFloat(getComputedStyle(document.documentElement).fontSize) const sizeOfSomeOtherElement = getRem(4) // returns the px value of 4rem const sizeOfThisElement = sizeOfSomeOtherElement * 0.5 </script> <div style="width: {sizeOfThisElement}rem"></div>

Now I understand in this particular example I can just use CSS calc, but let's pretend that I have to use a JavaScript value for the sake of argument. Here, I want to dynamically get the value of a rem as it could be different based on the user.

This will throw an error telling me that getComputedStyle is not defined, as window is not defined. Now, I could delay the rendering of the element and wait until the component is mounted once like so:

<script lang="ts"> let isLoaded = $state(false) onMount(() => { isLoaded = true }) /* ... other conditionals ... */ </script> {#if isLoaded) <div style="width: {sizeOfThisElement}rem"></div> {/if}

But this delays the rendering of the component by a slight, but noticeable amount. It doesn't seem to be the right solution to me, as I don't need the component to be mounted, I just need the variable window to be available, which I would think comes before mounting.

What's the best way to tackle this? Am I misunderstanding anything?

Answer

There will always be a delay between the server-side provided HTML being rendered and the component script being executed and updating it.

I tried other methods, that are focused on a single element (like bind:this and use:action), but they have the same issue.

You would essentially have to run code synchronously while the element is being rendered, to prevent this entirely. It is technically possible to add a plain <script> by wrapping it in an element but then you are limited to native JS and also cannot reference anything in the component script.

E.g.

<div> DIV to adjust <script> const getRem = rem => rem * parseFloat(getComputedStyle(document.documentElement).fontSize); const target = document.currentScript.parentElement; const sizeOfSomeOtherElement = getRem(4); target.style.width = (sizeOfSomeOtherElement * 0.5) + 'rem'; </script> </div>

Approaches like this are sometimes used for dealing with theme switches that are stored in localStorage to prevent flashes.

Related Articles