Search code examples
javascripthtmlcsssveltesveltekit

Enable and disable scroll based on boolean in SvelteKit?


The problem
In SvelteKit, I am trying to disable and enable scroll when my side menu is opened. To do that, I am trying to utilize the {open} variable that is bound to my menu icon with bind:open={open}. I want to trigger the function enableScroll() when open is true, and trigger the function disableScroll() when open is false. Both functions work fine. The {open} variable is true when I expect it to, and false when I expect it to when I use console.log(). The problem is that my if-block doesn't respond to {open} changing.

What I've tried

  • I have tried wrapping my if-block in another function and calling the function changeScroll() on:click.
  • I have tried adapting the conditionals: if === true, true, open || null; similarly for the negation.
  • I have tried using a ternary operator inside of the binding like so: on:click={open ? disableScroll() : enableScroll() }

Currently, my code looks like this:


    $: if (open === true) {
            onMount(() => {
                disableScroll()
            })
        } else if (open === false) {    
            onMount(() => {
                enableScroll()
            })
        }  

Perhaps I'm using onMount() wrongly? Or somehow I need to change my if statement to make it properly reactive / fix it some other way? I would gladly appreciate some help! Thank you.


Solution

  • onMount only queues a function to run once, when the component is mounted. So running onMount(() => { disableScroll() }) will not run disableScroll if the component has already been rendered -- any subsequent changes to open will not have any affect. This is why your code doesn't work.

    However, you do need to do something to prevent your scroll code from running on the server, since window isn't available there. In SvelteKit, you can determine whether you're in the browser by using the browser import from $app/env. This will tell you if it's safe to access browser-only APIs on the document or window. So, you could do something like the following:

    <script>
        import { onMount } from 'svelte';
        import { browser } from '$app/env';
    
        let open;
    
        let scrollTop = null;
        let scrollLeft = null;
    
        function disableScroll() {
            if (browser) {
                scrollTop = 
                    window.pageYOffset || window.document.documentElement.scrollTop;
                scrollLeft = 
                    window.pageXOffset || window.document.documentElement.scrollLeft,
                    window.onscroll = function() {
                    window.scrollTo(scrollLeft, scrollTop);
                }};
            }
    
        function enableScroll() {
            if (browser) {
                window.onscroll = function() {};
            }
        };
    
        $: if (open) {
            disableScroll();
        } else {
            enableScroll();
        }
    </script>