简体   繁体   English

在 Svelte 完成渲染 dom 后运行代码的正确方法是什么?

[英]What's the correct way to run code after Svelte is done rendering the dom?

I have a situation where I need to scroll a list item into view that is initially hidden.我有一种情况,我需要将列表项滚动到最初隐藏的视图中。 The issue is that if I don't use any delays the before calling scrollIntoView() on the list item to focus, the element is almost always null because it is not yet rendered.问题是,如果我在列表项上调用scrollIntoView()之前不使用任何延迟来聚焦,则该元素几乎总是 null 因为它尚未呈现。

A demo illustrating my issue: https://svelte.dev/repl/49c5b57097574017929649e08c001754?version=3.49.0说明我的问题的演示: https://svelte.dev/repl/49c5b57097574017929649e08c001754?version=3.49.0

I have found some workarounds:我找到了一些解决方法:

  1. The first one I tried was to wrap the scroll function in an arbitrary delay and hope the list is rendered after the delay, but it's not optimal because of different render speeds on different computers and not a very clean solution either (this is illustrated in the first demo above).我尝试的第一个是在任意延迟中包装滚动 function 并希望在延迟之后呈现列表,但这不是最佳的,因为不同计算机上的渲染速度不同,也不是一个非常干净的解决方案(这在上面的第一个演示)。

  2. The second one is better.第二个更好。 I wrapped the list in an element and added a transition and triggered the scroll on the introend event.我将列表包装在一个元素中并添加了一个转换并触发了introend事件的滚动。 This always seem to work, but it feels like a hack (albeit not a big hack).这似乎总是有效,但感觉就像一个黑客(虽然不是一个大黑客)。 Demo: https://svelte.dev/repl/c2583b73e376479a90fa185f2f3baa6e?version=3.49.0演示: https://svelte.dev/repl/c2583b73e376479a90fa185f2f3baa6e?version=3.49.0

Is workaround #2 the de facto way to do it, or is there another way to manage these types of situations where your JS is dependent on the dom state?解决方法#2 是事实上的方法,还是有另一种方法来管理您的 JS 依赖于 dom state 的这些类型的情况?

For DOM interactions like this, actions are a good fit.对于像这样的 DOM 交互, 动作非常适合。

Here you would add the action to the element containing the list, it will be triggered once the list is fully rendered.在这里,您将向包含列表的元素添加操作,一旦列表完全呈现,它将被触发。 If the action were to be added on an item, the action would trigger as soon as the item that should be focused is created and the centering would not work, because at that point there are no items after it.如果要在一个项目上添加操作,则该操作将在创建应该聚焦的项目后立即触发,并且居中将不起作用,因为此时它之后没有项目。

Eg例如

function focus(node) {
    node.querySelector('.focused-item')
        .scrollIntoView({ block: 'center' });
}
<section use:focus>
    {#each listItems as item}
        ...
    {/each}
</section>

REPL REPL

You can make this reactive by eg defining a variable that identifies the item and passing that to the action.您可以通过例如定义一个标识项目并将其传递给操作的变量来使其具有反应性。

let errorItem = 'item2';

// ...

function focus(node) {
    const update = () => {
        const item = node.querySelector('.focused-item');
        if (item)
            item.scrollIntoView({ block: 'center' });   
    }
    
    update();
    
    return { update };
}
<section use:focus={errorItem}>
    {#each listItems as item}
        <div class:focused-item={item === errorItem}>
            {item}  
        </div>
    {/each}
</section>

The parameter does not necessary have to be used in the action.该参数不必在操作中使用。 In this case it is just used to trigger the update function returned from the action.在这种情况下,它仅用于触发从操作返回的更新 function。

REPL REPL

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM