admin管理员组

文章数量:1396440

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.

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.

Share Improve this question asked Dec 3, 2021 at 23:15 KoenKoen 3864 silver badges10 bronze badges 7
  • 1 "...onMount, which runs after the ponent is first rendered to the DOM" Did you try it without onMount? Like this it would work – Corrl Commented Dec 4, 2021 at 3:22
  • 1 The problem is that I need to use onMount somehow to access the window, which is used in both functions. So the example works, but the functions themselves won't... – Koen Commented Dec 4, 2021 at 8:55
  • 1 Why is the window only accessibly inside onMount? Can you reproduce this in a REPL? – Corrl Commented Dec 4, 2021 at 9:25
  • 1 In the REPL it works, but in my local environment, and also after deploy, I get this: "ReferenceError: window is not defined". – Koen Commented Dec 4, 2021 at 9:39
  • 1 You can change $: open = open; to simply let open. I'm not sure about user experience when disabling scrolling like this. There's still the scrollbar and in the REPL it's possible to scroll a bit but it will instantly be reset which feels kind of strange and makes one wonder why it behaves like this. Why not disabling scroll via CSS instead like this? – Corrl Commented Dec 4, 2021 at 11:24
 |  Show 2 more ments

2 Answers 2

Reset to default 4

onMount only queues a function to run once, when the ponent is mounted. So running onMount(() => { disableScroll() }) will not run disableScroll if the ponent 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>

There is a pretty succinct way of doing this

<svelte:window on:wheel|nonpassive={e => {
    if(open)
        e.preventDefault()
}} />

and if you don't have a variable, for example if you wrap your side menu in a ponent you can skip the if condition

<svelte:window on:wheel|nonpassive|preventDefault />

i.e. block scroll if ponent is rendered.

and to go one step further in case you have a large side menu with the potential for overflow

<svelte:window on:wheel|nonpassive|preventDefault />
<SideMenu>
    <div on:wheel|stopPropagation>

will allow you to block all scroll events not related to your menu

本文标签: javascriptEnable and disable scroll based on boolean in SvelteKitStack Overflow