Svelte.js 3 Event Handling Tutorial

In this tutorial we learn how to handle events with one of Svelte's directives.

We cover event handling with one or more methods, the Javascript event object and how to use Svelte's event modifiers.

Lesson Video

If you prefer to learn visually, you can watch this lesson in video format.

Lesson Project

If you want to follow along with the examples in this lesson, you’ll need a Svelte app that was cloned with degit or scaffolded with Vite

If you already have an app from previous lessons, you can use that instead.

How to handle events in Svelte with the 'on' directive

Modern web applications will typically need to react to events that happen in the browser, like the user clicking a button.

To interact with events, Svelte gives us the on directive. A directive is a custom attribute that controls the element’s behavior in some way.

To use it, we prefix any standard DOM event with the on keyword, and separate the two with a : (colon).

As the directive’s value, we can use simple inline expressions or a function inside a pair of open-and-close curly braces.

Syntax: on directive
<element on:event_name={function/inline-expression} />

If we’re using an inline expression, we can use anonymous or arrow functions.

Syntax: inline expression
<!-- arrow function -->
<element on:event_name={ () => {} } />

<!-- anonymous function -->
<element on:event_name={ function() {} } />

As an example, we’ll create a button in the markup that changes a name constant when the user clicks on it.

We’ll use an inline expression to change the value.

Example: src/App.svelte
<script>
  let name = 'John'
</script>

<p>Hello, my name is {name}</p>

<button on:click={() => {name = 'Jane'}}>
  Change name
</button>

If we run the example in the browser and click on the button, it changes the name from “John” to “Jane”.

tip Even though inline event handlers doesn’t affect performance in Svelte like it does in other frameworks, we still want to separate the Logic from the View as much as we can. In that regard, it’s better to use standalone functions.

Event handling in Svelte with methods

We mentioned earlier that the on directive’s value can either be an inline expression, or a function we define in the script block.

When we use a function that doesn’t have any parameters, we can reference it. That is to say we can omit the parentheses at the end.

Syntax: event hanling methods
<element on:event_name={ function_name } />

As an example, let’s put our name change logic into a function and reference the function in the on binding.

Example: src/App.svelte
<script>
  let name = 'Chris Hemsworth'

  function changeName() {
    name = 'Thunder Chris'
  }
</script>

<p>Hello, my name is {name}</p>

<button on:click={ changeName }>
  Change name
</button>

If we click on the button in the browser, the name will change as we expect.

When an event function has one or more parameters, we need to invoke it within an arrow function. That’s because Svelte automatically passes an event object to the function.

Syntax: function with parameters
<element on:event_name={ function_name(param1, param2, ...) } />

To demonstrate method invoking, let’s use a parameter for the method instead of hardcoding the changing value.

Example: src/App.svelte
<script>
  let name = 'Chris Evans'

  function changeName(newName) {
    name = newName
  }
</script>

<p>Hello, my name is {name}</p>

<button on:click={ () => changeName('Captain Chris') }>
  Change name
</button>

How to use multiple methods on an event in Svelte

Svelte allows us to bind multiple functions to a single event. All we have to do is separate them with a comma inside the arrow function.

Syntax: multiple methods
<element on:event_name={
  () => {
    function_1(params),
    function_2(params)
  }
} />

To demonstrate, let’s change our example to include another variable that gets changed by a second method on the event.

Example: src/App.svelte
<script>
  let name = 'Chris Pratt'
  let ego = 'Actor'

  function changeName(newName) {
    name = newName
  }
  function changeEgo(newEgo) {
    ego = newEgo
  }
</script>

<p>Hello, my name is {name} ({ego})</p>

<button on:click={
  () => {
    changeName('Space Chris'),
    changeEgo('Starlord')
  }
}>
  Change name
</button>

When we click the button, both methods execute and the “name” as well as the “ego” changes.

note Events will be executed in the order they are specified in the directive.

How to use the Javascript Event Object in Svelte

Certain events may require us to access data about the event that occured through the Javascript Event Object.

The Event Object is a Javascript object that describes everything about the event that occured. For example, on a click event it will contain the exact x and y coordinates of where the mouse was clicked on the page, or whether the Ctrl or Alt keys were pressed during the click.

When we bind an event listener that executes a method, that method will automatically receive event (Event Object) as its first parameter. We can use it to access any event property or method .

Syntax: event parameter
funcName(event, other_params) {
  // access event object
  // through 'event' param
  event.property
}

If a method doesn’t have any parameters, we don’t need to specify event in the parameter list.

Syntax: event parameter
funcName() {
  // event is still
  // available
  event.property
}

To demonstrate, let’s create an example that displays the value of an input field in a paragraph.

We’ll bind to the input event on the text field and have it reference a function each time the user enters a character.

In the function, we can use the target attribute of the event object and find the value of what was typed into the input field.

Then we simply assign that value to a variable and display it in the template markup with string interpolation.

Example: src/App.svelte
<script>
  let name = ''

  function getInput(event) {
    // get the value of the input on the
    // event object and assign it to 'name'
    name = event.target.value
  }
</script>

<p>Your name: { name }</p>
<input type="text" on:input={getInput}>

If we run the example in the browser and enter a name in the input field, it will show the name in the paragraph as we type.

How to use the event object in Svelte with multi-parameter functions

We demonstrated earlier that when an event has one or more parameters, we need to invoke it in an arrow function because Svelte automatically passes the event object to it.

Example: src/App.svelte
<script>
  let name = 'Chris Evans'

  function changeName(newName) {
    name = newName
  }
</script>

<p>Hello, my name is {name}</p>

<button on:click={ () => changeName('Captain Chris') }>
  Change name
</button>

In that case, we didn’t need to use the event object. When we do need to use the event object however, we simply add it as the argument to the arrow function as well as the first argument of the event handling function.

Example: src/App.svelte
<script>
  let name = 'Chris Evans'

  function changeName(event, newName) {
    name = newName
    console.log(event)
  }
</script>

<p>Hello, my name is {name}</p>

<button on:click={ (event) => changeName(event, 'Captain Chris') }>
  Change name
</button>

If we run the example and click on the button, the name will change and the event object will be logged to the console. Everything works as expected and we can access the event object.

Event Modifiers in Svelte

Svelte allows us to connect a modifier to an event to change the event’s behavior.

As an example, let’s say our application contains a form. The default behavior for a form submission is to send an HTTP request to the server, refreshing the page and thus refreshing the application state. That means we lose any other data on the page that we may need to keep.

To demonstrate, let’s create a counter in the root App component with two functions that increment and decrement that count. We’ll also add a dummy form with a button that submits it.

Example: src/App.svelte
<script>
  let counter = 0

  function increment() { counter++ }
  function decrement() { counter-- }
</script>

<p>Count: { counter }</p>
<button on:click={ increment }>Increment</button>
<button on:click={ decrement }>Decrement</button>

<form>
  <p>
    <button>Submit Form</button>
  </p>
</form>

When we click on the Increment and Decrement buttons, the number changes as we would expect. But if we change the number from 0 and then click on the Submit button, the number resets to 0.

Typically, when working with forms, we want to prevent this default browser behavior. Instead we want to read the user input, validate it, then send it off to the server. We can’t do that if the value is lost.

Of course, there is a Javascript approach to solve this problem. We can simply add the Javascript event.preventDefault() method to a form handler function that’s referenced when the form is submitted.

Example: src/App.svelte
<script>
  let counter = 0

  function increment() { counter++ }
  function decrement() { counter-- }

  function formHandler(event) {
    // Prevent page refresh
    event.preventDefault()
    alert('Form submitted')
  }
</script>

<p>Count: { counter }</p>
<button on:click={ increment }>Increment</button>
<button on:click={ decrement }>Decrement</button>

<form>
  <p>
    <button on:click={ formHandler }>Submit Form</button>
  </p>
</form>

Now if we change the number and submit the form, the counter value won’t change.

This is a perfectly valid approach to solve the problem, but Svelte makes it a lot easier to do the same thing with modifiers.

How to use an event modifier in Svelte

An event modifier is chained onto the event with a | (pipe) operator.

Syntax: event modifier
<element on:event|modifier={ eventHandlerFunction } />

Our earlier example uses the preventDefault Javascript method. Its corresponding modifier is the preventDefault modifier, which is just Svelte using the event.preventDefault() function behind the scenes.

Let’s change the example and add the modifier.

Example: src/App.svelte
<script>
  let counter = 0

  function increment() { counter++ }
  function decrement() { counter-- }

  function formHandler(event) {
    alert('Form submitted')
  }
</script>

<p>Count: { counter }</p>
<button on:click={ increment }>Increment</button>
<button on:click={ decrement }>Decrement</button>

<form>
  <p>
    <button on:click|preventDefault={ formHandler }>
      Submit Form
    </button>
  </p>
</form>

If we change the number and submit the form, it works as expected. We did the exact same thing we could in Javascript, but faster and with less code.

Svelte also allows us to chain multiple modifiers to a single event by simply adding it.

To demonstrate, we’ll chain the once modifier to our example, which will remove the event handler after the first time it runs.

Example: src/App.svelte
<script>
  let counter = 0

  function increment() { counter++ }
  function decrement() { counter-- }

  function formHandler() {
    alert('Form submitted')
  }
</script>

<p>Count: { counter }</p>
<button on:click={ increment }>Increment</button>
<button on:click={ decrement }>Decrement</button>

<form>
  <p>
    <button on:click|preventDefault|once={ formHandler }>
      Submit Form
    </button>
  </p>
</form>

If we go to the browser and submit the form, it works as before. But if we try to submit the form again, the alert message won’t show and the page will refresh.

List of event modifiers in Svelte

Svelte provides us with the following modifiers to use with events.

ModifierDescription
passiveImproves scrolling performance on mousewheel/touch events
 tip Svelte will automatically add it when it’s safe to
nonpassiveExplicitly set passive to false
preventDefaultPrevent the browser from refreshing the page on form submission
stopPropagationPrevent the event from reaching the next element
captureFires the event handler during the capture phase instead of the bubbling phase
onceRemove the handler after the first time it runs
selfTrigger the handler only if the target is the element itself
trustedTrigger the handler only if the event is triggered by a user action