Home > Blockchain >  on:animationend can't access bind:this svelte (undefined)
on:animationend can't access bind:this svelte (undefined)

Time:10-02

I wanted to use querySelector() but I find that Svelte has another way to do this.

by using bind:this


the problem is that sometimes it works, but the most of time it will never work

by outputting undefined

here is a simple example:

<script>
  let thisDiv;

  console.log(divSelector) // ❌ "undefined" ✅ "<div>"
</script>

<div bind:this={thisDiv}>hello world</div>

the real bug

but the real problem is there

because I don't need to console.log for now

the things I wrote before are only for making you understand the bug
so I can start putting more information.

because I have a function that uses that variable
and call the function when the event "on:animationend" starts.

like this:

<div
  bind:this={thisDiv}
  on:animationend={doStuff()}
></div>

the problem is that animationend still console.log the thisDiv undefined

maybe is a problem of speed?

yes, ok...

but the animation in CSS only is 50ms long. and also tried with 0.5s

and CSS doesn't block the javascript, so js will continue to work when CSS is doing his animations (from what I know)

and 0.5s I think is a lot for doing a simple bind:this (querySelector)

how is it even possible? half second for getting a normal querySelector and still nothing.

enter image description here


source code

here the REPL online svelte source code example:

https://svelte.dev/repl/b49d019ce7f8426b80295158b091520f?version=3.50.1

make sure to uncomment the line 9 in child.svelte

...or see this directly

App.svelte

<script>
    import Child from "./Child.svelte"
    
    function generateExample() {
        let output = [];
        
        for(let i=0; i<=100; i  ) {
            output = [
                ...output, `text ${i}`
            ]
        }
        
        return output;
    }
    
  let array = generateExample()
</script>

{#each array as item, index}
   <Child {index}>
      {item}
   </Child>
{/each}

Child.svelte

<script>
  export let index;
  const DELAY = 50;
  
  let thisDiv;

    // UNCOMMENT THE NEXT LINE
  function doStuff() {
         // thisDiv.scrollIntoView(); 
  }
</script>

<div
  style="--delay:{index * DELAY}ms;"
  bind:this={thisDiv}
  on:animationend={doStuff()}
  >
  <slot></slot>
</div>

<style>
  div {
    animation: show var(--delay);
  }

  @keyframes show {
    from {
      translate: 100vw;
    }
    to {
      translate: 0;
    }
  }
</style>

CodePudding user response:

Actually, your code is just faulty, you called the handler instead of referencing it:

on:animationend={doStuff()}

Should be:

on:animationend={doStuff}

REPL

Also, you do not need bind:this and could use the event object:

function doStuff(e) {
    e.target.scrollIntoView();
}

REPL

CodePudding user response:

code of the solution:

<script>
// other your code

$: if(thisDiv) {
  thisDiv.addEventListener("animationend", () => {
    thisDiv.scrollIntoView()
  })
}
</script>

and is also bug free, because of if checking and native javascript code.


the bug is happening because when you write bind:this, the value isn't available directly (but this doesn't mean is slow)


so how to solve it?

I believe that $: if(thisDiv) should make it work.

so put all the logic inside the IF


this means also that you need to not use the attribute on:animationend anymore since it isn't inside the IF


so we reversed the logic,

  • first, we make sure that the bind isn't undefined,
  • then we use vanilla javascript to do the logic (instead of svelte logic)

EDIT:

I saw now that @H.B suggest an easier way, because your bug was only in a typo

make sure to accept his answer.

doStuff()doStuff

CodePudding user response:

The problem is that animationend is fired immediately. See my other answer.

Try just logging the index/time:

function doStuff() {
    console.log(index, 'end', new Date());
}

Would recommend adding the event listener in onMount (from 'svelte') and clean it up again on destroy:

onMount(() => {
    thisDiv.addEventListener('animationend', doStuff);
    
    () => thisDiv.removeEventListener('animationend', doStuff);
})

REPL

  • Related