4.4 KiB
title |
---|
Events |
In most applications, you'll need to respond to the user's actions. In Svelte, this is done with the on:[event]
directive.
Element events
When used on an element, on:click={handler}
is equivalent to calling element.addEventListener('click', handler)
. When the element is removed, Svelte calls removeEventListener
automatically.
<!-- { title: 'Inline event handlers' } -->
<p>Count: {count}</p>
<button on:click="{() => count += 1}">+1</button>
/* { hidden: true } */
{
count: 0
}
For more complicated behaviours, you'll probably want to declare an event handler in your <script>
block:
<!-- { title: 'Event handlers' } -->
<script>
let count = 0;
function incrementOrDecrement(event) {
const d = event.shiftKey
? -1
: +1;
count += d;
}
</script>
<p>Count: {count}</p>
<button on:click={incrementOrDecrement}>update</button>
/* { hidden: true } */
{
count: 0
}
Event handler modifiers
While you can invoke methods like event.stopPropagation
directly...
<!-- { repl: false } -->
<div on:click="{e => e.stopPropagation()}">...</div>
...it gets annoying if you want to combine that with some other behaviour:
<!-- { repl: false } -->
<script>
let foo = false;
function toggleFoo(event) {
event.stopPropagation();
event.preventDefault();
foo = !foo;
}
</script>
<div on:click={toggleFoo}>...</div>
For that reason, Svelte lets you use event modifiers:
preventDefault
stopPropagation
passive
— improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)once
— removes the listener after the first invocationcapture
passive
andonce
are not implemented inlegacy
mode
The example above can be achieved with modifiers — no need for a separate event handler:
<!-- { repl: false } -->
<div on:click|stopPropagation|preventDefault="{() => foo = !foo}">...</div>
Component events
Events are an excellent way for nested components to communicate with their parents. Let's revisit our earlier example, but turn it into a <CategoryChooser>
component:
<!-- { filename: 'CategoryChooser.html', repl: false } -->
<p>Select a category:</p>
{#each categories as category}
<button on:click="fire('select', { category })">select {category}</button>
{/each}
<script>
export default {
data() {
return {
categories: [
'animal',
'vegetable',
'mineral'
]
}
}
};
</script>
When the user clicks a button, the component will fire a select
event, where the event
object has a category
property. Any component that nests <CategoryChooser>
can listen for events like so:
<!--{ title: 'Component events' }-->
<CategoryChooser on:select="playTwentyQuestions(event.category)"/>
<script>
import CategoryChooser from './CategoryChooser.html';
export default {
components: {
CategoryChooser
},
methods: {
playTwentyQuestions(category) {
alert(`ok! you chose ${category}`);
}
}
};
</script>
<!--{ filename: 'CategoryChooser.html', hidden: true }-->
<p>Select a category:</p>
{#each categories as category}
<button on:click="fire('select', { category })">select {category}</button>
{/each}
<script>
export default {
data() {
return {
categories: [
'animal',
'vegetable',
'mineral'
]
}
}
};
</script>
Just as this
in an element's event handler refers to the element itself, in a component event handler this
refers to the component firing the event.
There is also a shorthand for listening for and re-firing an event unchanged.
<!-- { repl: false } -->
<!-- these are equivalent -->
<Widget on:foo="fire('foo', event)"/>
<Widget on:foo/>
Since component events do not propagate as DOM events do, this can be used to pass events through intermediate components. This shorthand technique also applies to element events (on:click
is equivalent to on:click="fire('click', event)"
).