sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.04k stars 4.08k forks source link

Transition not removing extra DOM nodes after completion #7119

Open probablykasper opened 2 years ago

probablykasper commented 2 years ago

Describe the bug

When you have an element with a transition and destroy it, but then recreate it before the transition is done, you can end up with old elements not being removed.

Reproduction

REPL

When you type in the input field, the value is set to what you typed if the value is empty, otherwise the value is cleared. Try randomly spamming your keyboard, and a bunch of extra elements should be left there.

Logs

No response

System Info

System:
    OS: macOS 10.15.7
    CPU: (8) x64 Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz
    Memory: 1.35 GB / 32.00 GB
    Shell: 3.2.2 - /usr/local/bin/fish
  Binaries:
    Node: 16.13.1 - /var/folders/gs/3qcm1mc918s08sxvkj6zj2rr0000gp/T/fnm_multishells/87890_1641758181492/bin/node
    Yarn: 1.22.17 - /var/folders/gs/3qcm1mc918s08sxvkj6zj2rr0000gp/T/fnm_multishells/87890_1641758181492/bin/yarn
    npm: 8.1.2 - /var/folders/gs/3qcm1mc918s08sxvkj6zj2rr0000gp/T/fnm_multishells/87890_1641758181492/bin/npm
  Browsers:
    Brave Browser: 96.1.32.106
    Chrome: 96.0.4664.110
    Firefox: 95.0.2
    Safari: 15.2
  npmPackages:
    svelte: ^3.45.0 => 3.45.0

Severity

annoyance

probablykasper commented 2 years ago

This issue might be what's causing https://github.com/sveltejs/svelte/issues/1591

homerjam commented 2 years ago

A possible cause of this issue is an active transition belonging to a child component, in this case applying the local modifier may resolve the issue.

https://svelte.dev/tutorial/local-transitions https://svelte.dev/docs#template-syntax-element-directives-transition-fn

leumasme commented 6 months ago

Bumping this with another repro for what looks like the same underlying issue:

When having an If Block with an Each block inside it that creates a component, like

{#if active.length > 0}
    <div transition:fly={{ x: -10, duration: 1000 }}>
        {#each active as mod}
            <Mod mod={mod}/>
        {/each}
    </div>
{/if}

Changing the active array to have some elements like [1,2,3,4,5,6]; then changing it to be empty []; and then while the remove transition from the if block is playing, changing it to have elements again but be shorter than the original array ["aaa","bbb"], some elements generated from the first array will remain visible, as if the array had the contents ["aaa","bbb",3,4,5,6]
Repro: https://svelte.dev/repl/b11e64abca0141689162c85954a06920?version=4.2.10
When clicking the button slowly, it will switch between no elements; elements for [1,2,3,4,5,6]; elements for ["aaa","bbb"].
When clicking the button quickly (quicker than the 1s animation time), it will switch between no elements; elements for [1,2,3,4,5,6]; elements for ["aaa","bbb",3,4,5,6] (Incorrect behavior!).

The same code seems to work correctly in the Svelte5 playground.

The behavior also does not occour if the content of the each block is a normal Element instead of a Svelte component.
The problem is not solved by using a keyed each block (this just leads to the new elements not overwriting the old ones, instead producing HTML for ["aaa","bbb",1,2,3,4,5,6], which is still incorrect).

Help thread on the Svelte Discord about this can be found here

that's definitely a bug on the diffing for the transition where the elements don't experience a keyed "destroy"