Why cant this Astro/Svelte component access the document element?

<script lang="ts">
  let theme = localStorage.getItem("theme") ?? "light";
  const handleClick = () => {
    theme = (theme === "light" ? "dark" : "light");    
    console.log('classlist',document.documentElement.classList);    
  };
  $:{
    if (theme === "dark") {
      document!.documentElement.classList.add("dark");
    } else {
      document!.documentElement.classList.remove("dark");
    }
    localStorage.setItem("theme", theme);
  }
</script>

<button on:click={handleClick}>{theme === "light" ? "🌙" : "🌞"}</button>

when the button is clicked it prints the classlist fine, but I get document not defined when i add the reactive value.

Thank you @H.B for the answer:

<script lang="ts">
  import { onMount } from "svelte";
  let theme = localStorage.getItem("theme") ?? "light";  
  let flag = false;
  onMount(()=>{
    flag = true
  }) 
  $: if (flag) {
    if ( theme === 'dark') {
      document!.documentElement.classList.add("dark");
    } else {
      document!.documentElement.classList.remove("dark");
    }
    localStorage.setItem("theme", theme);
  }
  const handleClick = () => {
    theme = (theme === "light" ? "dark" : "light");    
    console.log('classlist',document.documentElement.classList);    
  }; 
</script>

<button on:click={handleClick}>{theme === "light" ? "🌙" : "🌞"}</button>

this works as intended

Upvotes: 2

Views: 1877

Answers (1)

brunnerh
brunnerh

Reputation: 185140

My guess would be that your build is server-side rendering. During SSR you cannot access any browser specific objects like the window or document.

You can e.g. try to use onMount and set a flag to enable the reactive statement (add an if for that after $:).

Upvotes: 3

Related Questions