Svelte中{#each}块添加新项后如何滚动到最后一项?
在Svelte中获取循环最后一项引用并实现自动滚动
问题场景
我通过以下代码在Svelte中渲染messages数组:
{#each messages as message, i} {@const isLast = i === messages.length - 1} <div class="card" class:active={isLast}> {message} </div> {/each}
同时编写了追加数组元素的函数:
function addNextWord(){ messages = [...messages, words[index]]; }
执行addNextWord后,需要将新添加的最后一项滚动到视图中(使用Element.scrollIntoView),但不知道如何获取循环中最后一项的DOM引用。
解决方法
方法1:条件绑定DOM引用
给循环中的最后一项单独绑定DOM引用,仅当元素是最后一项时赋值:
<script> import { tick } from 'svelte'; let messages = []; let words = ['foo', 'bar', 'baz']; let index = 0; let lastCard; function addNextWord(){ messages = [...messages, words[index]]; index = (index + 1) % words.length; // 等待Svelte异步更新DOM后执行滚动 tick().then(() => { lastCard?.scrollIntoView({ behavior: 'smooth' }); }); } </script> {#each messages as message, i} {@const isLast = i === messages.length - 1} <div class="card" class:active={isLast} bind:this={isLast ? lastCard : null} > {message} </div> {/each} <button on:click={addNextWord}>添加元素</button>
核心逻辑:通过bind:this={isLast ? lastCard : null}只给最后一项绑定引用,再用tick()等待DOM更新完成后调用滚动方法。
方法2:绑定所有项的引用数组
给循环中的每个元素绑定引用到数组,之后直接取数组最后一项:
<script> import { tick } from 'svelte'; let messages = []; let words = ['foo', 'bar', 'baz']; let index = 0; let cardRefs = []; function addNextWord(){ messages = [...messages, words[index]]; index = (index + 1) % words.length; tick().then(() => { const lastRef = cardRefs.at(-1); lastRef?.scrollIntoView({ behavior: 'smooth' }); }); } </script> {#each messages as message, i (i)} <div class="card" class:active={i === messages.length - 1} bind:this={cardRefs[i]} > {message} </div> {/each} <button on:click={addNextWord}>添加元素</button>
这种方式会保存所有元素的引用,适合需要操作多个循环项的场景,注意要给each块加上键值(i)确保引用数组和元素对应。
方法3:利用生命周期钩子+选择器
如果已经给最后一项添加了active类,可以通过afterUpdate钩子自动检测并滚动:
<script> import { afterUpdate } from 'svelte'; let messages = []; let words = ['foo', 'bar', 'baz']; let index = 0; function addNextWord(){ messages = [...messages, words[index]]; index = (index + 1) % words.length; } afterUpdate(() => { const lastCard = document.querySelector('.card.active'); lastCard?.scrollIntoView({ behavior: 'smooth' }); }); </script> {#each messages as message, i} {@const isLast = i === messages.length - 1} <div class="card" class:active={isLast}> {message} </div> {/each} <button on:click={addNextWord}>添加元素</button>
这种方式不需要手动绑定引用,但依赖类选择器的唯一性,适合样式已经区分最后一项的场景。
内容的提问来源于stack exchange,提问作者KyleMit
相关产品推荐
相关产品推荐

