Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I use Svelte Reactivity with DOM getElementById?

I have a div element where it is scrollable

    <script>
        let scrollBoxObj;
        $: scrollBoxObj = document.getElementById("chat-box");
        
        $: if (!(scrollBoxObj === undefined) && scrollBoxObj.scrollTop < scrollBoxObj.scrollHeight) {
            scrollBoxObj.scrollTop = scrollBoxObj.scrollHeight;
        }
    </script>
    <div id="scrollBox" class="h-screen w-auto chat-box border border-orange rounded">
        <div id="chat-box" style="margin: 0" class="chat-box">
            {#each chatBox as { user, content, type}}
                <MessageBox {user} message={content} {type} />
            {/each}
        </div>
    </div>

    <style>
        .chat-box {
            overflow-y: auto;
        }
    </style>

i am trying to auto scroll down when a new message is added. but it is not reactive. or i didn't understand how reactivity works in svelte. i also tried to assign scrollBoxObj in onMount but it was still the same result didn't work.

like image 259
AK-35 Avatar asked Mar 02 '23 18:03

AK-35


1 Answers

In Svelte, the reactivity from $: x = y kicks in when there is a change on the left side from the equation (the y-part).

In your code you have

$: scrollBoxObj = document.getElementById("chat-box");

On the left side we have a string ("chat-box") which is a constant and will never change, there is also the document which will also never change, this means the reactivity will not work as you expect.

When working with Svelte using document.getElementById (or any of the other selector methods) is considered bad practice because you are interacting directly with the DOM, while your DOM should rather be a reflection of your state. In short: don't do it.

Svelte provides you with an easy way to bind a DOM-node (like a div) to a variable by simply adding bind:this={variable}

In your case this would become:

<script>
 let scrollbox
</script>

<div bind:this={scrollbox}>
 ...
</div>

The reactive declaration (the if block) will execute whenever anything inside it changes, which is scrollbox in your case:

$: if (scrollBoxObj && scrollBoxObj.scrollTop < scrollBoxObj.scrollHeight) {
  scrollBoxObj.scrollTop = scrollBoxObj.scrollHeight;
}

Just note here again that this will only trigger when scrollbox changes, once it is bound you will not retrigger (for instance when the user scrolls, it will not do anything) for that you should probably use an on:scroll event.

like image 86
Stephane Vanraes Avatar answered Mar 04 '23 07:03

Stephane Vanraes