Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add event directive to svelte html expression?

I want to add a custom button through an html expression in svelte. Through the docs here, I can do that using the @html tag.

{@html 
`
 <button>
   click me
 </button>
`
}

I would like to now add a directive event handler to the html string, namely a locally defined function that will be called when button is clicked. What I have tried so far:

{@html 
`
 <button
   on:click="${()=>doSomething()}">
   click me
 </button>
`
}

However, as I expected it didn't work. I was thinking there could be another way to this, but couldn't come up with one so far

like image 382
ndricim_rr Avatar asked Sep 08 '25 00:09

ndricim_rr


1 Answers

Conceptually, using the @html directive is equivalent to doing el.innerHTML = myHtmlString.

Said otherwise, in the context of your HTML string, you're completely out of Svelte's realm (and completely in native browser's realm).

on:click is special Svelte syntax, the browser won't process that. So if you want events in you HTML string, you need to go back to raw HTML/JS. That is, use onclick instead.

Of course, the semantics of onclick are completely different from that of on:click. To onclick, you can only pass a string, that will be eval'd when the event happens (whereas with on:click, you pass a function reference, that will be called when the event happens).

To make this very clear, let's recall Svelte's syntax:

<button on:click={doSomething}>Click me</button>

In contrast, in raw HTML, this would look like:

{@html '<button onclick="doSomething()">Click click!</button>'}

See the important difference: with onclick, you need to call the doSomething() function, or nothing will happen on click.

Now, extra fun: this doSomething function needs to be available in scope... That is, you have to attach it to window:

<script context="module">
  // note: using context=module to avoid adding this function to window
  //       multiple times
  window.doSomething = () => { ... }
</script>
{@html '<button onclick="doSomething()">Click click!</button>'}

This is ugly... You could do something like this instead, to avoid polluting the global scope:

<script>
  import { onMount } from 'svelte'

    const doSomething = () => { ... }

    onMount(() => {
        const btn = document.querySelector('#grab-me')
        btn.addEventListener('click', doSomething)
    })
</script>

{@html '<button id="grab-me">Click click!</button>'}

Now we're polluting DOM ids... We could further avoid that by wrapping with an element that Svelte controls, to which we could grab a reference with bind:this={el}...

To sum it up, dynamic elements in @html strings are best avoided because you lose all the benefits of Svelte inside them. @html is best fitted for rendering static HTML content.

Why do you want to put your button in a string, instead of letting Svelte manage it?

If you really need going done this road, you might also be interested in this dirty trick, that I absolutely don't recommend to use (I would never use it in my own code)... But this might give you further perspective on what you're trying to achieve.

like image 173
rixo Avatar answered Sep 09 '25 17:09

rixo