skip to Main Content

In svelte, I’m currently templating out an array of messages like this:

{#each messages as message, i}
    {@const isLast = i === messages.length - 1}
    
    <div class="card" class:active={isLast}>
        {message}
    </div>

{/each}

I then have a function where I can append a new item to the array

function addNextWord(){
    messages = [...messages, words[index]];
}

When I do so, I’d like to scroll the very last item into view (using something like Element.scrollIntoView).

But how can I get a reference to just the last item in the loop?

Demo in Svelte REPL

2

Answers


  1. Chosen as BEST ANSWER

    As far as I can tell, you can't conditionally set a binding on an element, so the two strategies would be to set a bind:this on the parent container or on an array of child items and then pick what one you want from there.

    Using the bind:this directive allows you to reference a DOM element from a variable in svelte.

    Once you have a reference to the parent you can use Element.lastElementChild to get the last child from a parent element.

    Finally, you'll have to wait for a tick for the data changes to propagate to the DOM before selecting the last item.

    Put all together, it should look something like this:

    <script>
        import { tick } from 'svelte';
        
        let messages = ["Hello", "World"];
        let listContainer;
        
        async function addNextWord(){
            messages = [...messages, words[index]];
            await tick();
            listContainer.lastChild.scrollIntoView({ behavior: "smooth"});
        }
    </script>
    
    <div bind:this={listContainer}>
        {#each messages as message, i}
            {@const isLast = i === messages.length - 1}
            
            <div class="card" class:active={isLast}>
                {message}
            </div>
        
        {/each}
    </div>
    

    Demo in Svelte REPL

    See Also: How to scroll to the last item when adding a new one on a list?


  2. Just use an action.

    const initialCount = messages.length;
    const scroll = node => {
        if (messages.length > initialCount)
            node.scrollIntoView()
    }
    
    <div class="card" use:scroll ...
    

    REPL

    In the vast majority of cases actions are just better than bind:this.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search