This is how you add event listener in Svelte
<script>
function onClick(event) {
}
</script>
<button on:click={onClick} />
Under the hood, it is calling addEventListener()
from the DOM API.
The addEventListener()
takes in 2 parameters, the event type and the listener; and optionally takes in a 3rd parameter, which is the option
object.
The option
is where you can specify whether to set up the event listener in capture
phase, or making it passive
. But how do you pass in this option in Svelte?
...
Introducing the Event Modifiers!
Event Modifiers help pass in options to your addEventListener
, as well as simplify common patterns in event listener.
Here are the 7 event modifiers you must know!
1. preventDefault
How often you need to call event.preventDefault()
to prevent default behavior from the browser? prevent form from submitting, prevent browser disallow drag-and-drop, ...
The preventDefault
modifier helps you do that easily.
<script>
function onClick(event) {}
</script>
<button on:click|preventDefault={onClick} />
This is equivalent of calling event.preventDefault() on the start of the event handler
<script>
function onClick(event) {}
function onClickEquivalent(event) {
event.preventDefault();
onClick(event);
}
</script>
<button on:click={onClickEquivalent} />
2. stopPropagation
Have you used event.stopPropagation
to prevent event from bubbling up to its parents?
You can easily do that with the stopPropagation
modifier!
<script>
function onClick(event) {}
</script>
<button on:click|stopPropagation={onClick} />
This is equivalent of calling event.stopPropagation()
on the start of the event handler
<script>
function onClick(event) {}
function onClickEquivalent(event) {
event.stopPropagation();
onClick(event);
}
</script>
<button on:click={onClickEquivalent} />
3. passive
For performance reasons, you may want to specify { passive: true }
to indicate to the browser that you will not call event.preventDefault()
. This is often used to improve the scrolling performance for scroll event listener
You can do this with the passive
modifier
<script>
function onScroll(event) {}
</script>
<div on:scroll|passive={onScroll} />
This is equivalent of calling addEventListener()
with { passive: true }
<script>
function onScroll(event) {}
let element;
onMount(() => {
element.addEventListener('scroll', onScroll, { passive: true });
return () => {
element.removeEventListener('scroll', onScroll, { passive: true });
}
});
</script>
<div bind:this={element} />
4. nonpassive
passive: true
is great, and Chrome is making them passive
by default if you do not pass in the option for passive
.
If you want to opt this out, you'll have to specify { passive: false }
which leads to the nonpassive
event modifier
<script>
function onScroll(event) {}
</script>
<div on:scroll|nonpassive={onScroll} />
This is equivalent of calling addEventListener()
with { passive: false }
<script>
function onScroll(event) {}
let element;
onMount(() => {
element.addEventListener('scroll', onScroll, { passive: false });
return () => {
element.removeEventListener('scroll', onScroll, { passive: false });
}
});
</script>
<div bind:this={element} />
5. once
Sometimes you want the event listener to be called only once. You want to remove the event listener as soon as it's being called.
The once
modifier helps you do that
<script>
function onClick(event) {}
</script>
<button on:click|once={onClick} />
This is equivalent of calling addEventListener()
with { once: true }
<script>
function onClick(event) {}
let element;
onMount(() => {
element.addEventListener('click', onClick, { once: true });
return () => {
element.removeEventListener('click', onClick, { once: true });
}
});
</script>
<button bind:this={element} />
6. capture
Sometimes, you want to register the event listener for the capturing phase.
You can do it with the capture
event modifier
<script>
function onClick(event) {}
</script>
<button on:click|capture={onClick} />
This is equivalent of calling addEventListener()
with { capture: true }
<script>
function onClick(event) {}
let element;
onMount(() => {
element.addEventListener('click', onClick, { capture: true });
return () => {
element.removeEventListener('click', onClick, { capture: true });
}
});
</script>
<button bind:this={element} />
7. self
Lastly, you want the event listener to be called only when the event is dispatched from the element itself, and NOT from its children.
You do that with the self
event modifier
<script>
function onClick(event) {}
</script>
<button on:click|self={onClick} />
This is equivalent of checking if the event.target
=== current element
<script>
function onClick(event) {}
let element;
function onClickEquivalent(event) {
if (event.target === element) {
onClick(event);
}
}
</script>
<button bind:this={element} on:click={onClickEquivalent} />
Event modifiers can be chained
There are times where you want to apply more than 1 event modifiers, and YES you can do that.
<button on:click|self|stopPropagation|preventDefault={onClick} />
You can chain as many event modifiers as you like, and the order does not matter.
However, by the nature of passive
and preventDefault
, you can't use them together
<button on:click|passive|preventDefault={onClick} />
<!-- Error: The 'passive' and 'preventDefault' modifiers cannot be used together -->
Do you learn better with video? I've made a video explaining how to add event listeners and cover each of the event modifiers in depth with examples
Top comments (1)
Excellent article as usual
Just a question, how can I use these event modifiers in my own components?
I want to make a custom button but I can't use these event modifiers, like this:
Or at least how can I optionally apply those modifiers, so I can make something like