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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With