Handling Component and DOM Events in Svelte Apps

John Au-Yeung - Jan 28 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Svelte is an up and coming front end framework for developing front end web apps.

It’s simple to use and lets us create results fast.

In this article, we’ll look at how to handle DOM events in Svelte components.

Events

We can use the on: directive to handle events. For instance, we can write the following to get the mouse position as we move our mouse around the screen:

App.svelte :

<script>
  let pos = { x: 0, y: 0 };

  const handleMousemove = event => {
    pos.x = event.clientX;
    pos.y = event.clientY;
  };
</script>

<style>
  div {
    width: 100vw;
    height: 100vh;
  }
</style>

<div on:mousemove={handleMousemove}>
  The mouse position is {pos.x} x {pos.y}
</div>
Enter fullscreen mode Exit fullscreen mode

In the code above, we have on:mousemove={handleMousemove} to attach the handleMousemove handler to our div.

The handleMousemove function has the event parameter, which is the MouseEvent object.

Therefore, we can access the mouse position with the clientX and clientY properties for the x and y position of the mouse respectively.

In the end, we display them on the screen.

Event handlers can also be declared inline as follows:

App.svelte :

<script>
  let pos = { x: 0, y: 0 };
</script>

<style>
  div {
    width: 100vw;
    height: 100vh;
  }
</style>

<div on:mousemove={event => {
    pos.x = event.clientX;
    pos.y = event.clientY;
  }}>
  The mouse position is {pos.x} x {pos.y}
</div>
Enter fullscreen mode Exit fullscreen mode

Or we can put them between quotes as follows:

App.svelte :

<script>
  let pos = { x: 0, y: 0 };
</script>

<style>
  div {
    width: 100vw;
    height: 100vh;
  }
</style>

<div on:mousemove="{event => {
    pos.x = event.clientX;
    pos.y = event.clientY;
  }}">
  The mouse position is {pos.x} x {pos.y}
</div>
Enter fullscreen mode Exit fullscreen mode

The quotes provide syntax highlighting in some environments, but they’re optional.

Inline event handler doesn’t impact the performance of the app since Svelte will optimize it by not attaching and detaching on the fly.

Modifiers

DOM event directives can have modifiers attached to them to change their behavior.

For instance, we can add the once modifier to the on:click directive to only listen to mouse click of an element once:

App.svelte :

<script>
  const handleClick = () => {
    alert("alert");
  };
</script>

<button on:click|once={handleClick}>
  Click Me Once
</button>
Enter fullscreen mode Exit fullscreen mode

The once modifier in the code above only runs the handleClick handler once, so if we click the button again, it’ll run nothing.

Other modifiers include:

  • preventDefault — calls event.preventDefault before running the handler
  • stopPropagation — calls event.stopPropagation to prevent the event from reaching the next element
  • passive — improves scrolling performance on touch or wheel events
  • capture — fires handlers during the capture phase instead of the bubbling phase
  • self — only trigger handler if event.target is the element itself.

They can also be chained together like on:click|once|self .

Photo by Marvin Meyer on Unsplash

Component Events

Components can also dispatch events. For instance, we can write the following code to dispatch an event from a child to a parent:

App.svelte :

<script>
  import Button from "./Button.svelte";
  const handleGreet = event => {
    alert(event.detail.text);
  };
</script>

<Button on:greet={handleGreet} />
Enter fullscreen mode Exit fullscreen mode

Button.svelte :

<script>
  import { createEventDispatcher } from "svelte";

  const dispatch = createEventDispatcher();

  const greet = () => {
    dispatch("greet", {
      text: "Hello Mary"
    });
  };
</script>

<button on:click={greet}>
  Send Greeting
</button>
Enter fullscreen mode Exit fullscreen mode

In Button, we create the dispatch constant with the createEventDispatcher function. dispatch is a function, which we call with the event name and the payload to send with the event emission as the arguments.

Then in App , we listen to the greet event emitted from Button by attaching the handleGreet event listener to the on:greet directive.

When handleGreet runs after the greet event is emitted, then the event object has the data sent with the event emission and we retrieved it with event.detail.text .

Therefore, when we click the Send Greeting button, we see ‘Hello Mary’ displayed in an alert box.

Event Forwarding

Component events don’t bubble like DOM events. To forward them to another component, we can add a forward function, which calls dispatch .

Then we can listen to the event in the component where the event is forwarded to.

For instance, we can write:

Button.svelte :

<script>
  import { createEventDispatcher } from "svelte";

  const dispatch = createEventDispatcher();

  const greet = () => {
    dispatch("greet", {
      text: "Hello Mary"
    });
  };
</script>

<button on:click={greet}>
  Send Greeting
</button>
Enter fullscreen mode Exit fullscreen mode

Parent.svelte :

<script>
  import Button from "./Button.svelte";
  import { createEventDispatcher } from "svelte";

  const dispatch = createEventDispatcher();

  const forward = () => {
    dispatch("greet", event.detail);
  };
</script>

<Button on:greet />
Enter fullscreen mode Exit fullscreen mode

App.svelte :

<script>
  import Parent from "./Parent.svelte";
  const handleGreet = event => {
    alert(event.detail.text);
  };
</script>

<Parent on:greet={handleGreet} />
Enter fullscreen mode Exit fullscreen mode

In the code above, Button.svelte emits the greet event, then the Parent.svelte component forwards the event, then App.svelte listens to the greet event.

In Parent.svelte ,

<Button on:greet />
Enter fullscreen mode Exit fullscreen mode

is the same as:

<Button on:greet={forward} />
Enter fullscreen mode Exit fullscreen mode

DOM Event Forwarding

We can forward DOM events like component events. For instance, we can write:

Button.svelte :

<button on:click>
  Send Greeting
</button>
Enter fullscreen mode Exit fullscreen mode

Parent.svelte :

<script>
  import Button from "./Button.svelte";
</script>

<Button on:click />
Enter fullscreen mode Exit fullscreen mode

App.svelte :

<script>
  import Parent from "./Parent.svelte";
  const handleClick = event => {
    alert("Hello");
  };
</script>

<Parent on:click={handleClick} />
Enter fullscreen mode Exit fullscreen mode

The code above forwards the click event from Buttonto Parent and Parent to App .

Therefore, we’ll see a ‘Hello’ alert displayed on the screen when we click the Send Greeting button.

Conclusion

Svelte components and DOM elements can have event handlers attached to them.

We listen to events using the on: directives with modifiers.

Events can be forwarded with the on: directive without any value set to it.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .