Adding Lifecycle Hooks to Elements with Svelte Actions
Tags: svelte, webdev, programming, javascript
Published: 2021-01-28 21:37:02

**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 use actions to add lifecycle hooks to elements of our choice.

Actions
=======

Actions are element level lifecycle functions. They’re useful for that needs to listen to element events like tooltips, and interfacing with third-party libraries.

For example, we can use it to make a box that bounces when we drag it as follows:

`pannable.js` :

```
export const pannable = node => {  
  let x;  
  let y; const handleMousedown = event => {  
    x = event.clientX;  
    y = event.clientY; node.dispatchEvent(  
      new CustomEvent("panstart", {  
        detail: { x, y }  
      })  
    ); 
    window.addEventListener("mousemove", handleMousemove);  
    window.addEventListener("mouseup", handleMouseup);  
  }; 

  const handleMousemove = event => {  
    const dx = event.clientX - x;  
    const dy = event.clientY - y;  
    x = event.clientX;  
    y = event.clientY; node.dispatchEvent(  
      new CustomEvent("panmove", {  
        detail: { x, y, dx, dy }  
      })  
    );  
  }; 

  const handleMouseup = event => {  
    x = event.clientX;  
    y = event.clientY; node.dispatchEvent(  
      new CustomEvent("panend", {  
        detail: { x, y }  
      })  
    ); 
    window.removeEventListener("mousemove", handleMousemove);  
    window.removeEventListener("mouseup", handleMouseup);  
  }; 

  node.addEventListener("mousedown", handleMousedown); return {  
    destroy() {  
      node.removeEventListener("mousedown", handleMousedown);  
    }  
  };  
};
```

`App.svelte` :

```
<script>  
  import { spring } from "svelte/motion";  
  import { pannable } from "./pannable.js"; 

  const coords = spring(  
    { x: 0, y: 0 },  
    {  
      stiffness: 0.2,  
      damping: 0.4  
    }  
  ); 

  const handlePanStart = () => {  
    coords.stiffness = coords.damping = 1;  
  }; 

  const handlePanMove = event => {  
    coords.update($coords => ({  
      x: $coords.x + event.detail.dx,  
      y: $coords.y + event.detail.dy  
    }));  
  }; 

  const handlePanEnd = event => {  
    coords.stiffness = 0.2;  
    coords.damping = 0.4;  
    coords.set({ x: 0, y: 0 });  
  };  
</script>

<style>  
  .box {  
    position: absolute;  
    width: 100px;  
    height: 100px;  
    border-radius: 4px;  
    background-color: green;  
    cursor: move;  
  }  
</style>

<div class="box"  
  use:pannable  
  on:panstart={handlePanStart}  
  on:panmove={handlePanMove}  
  on:panend={handlePanEnd}  
  style="transform:  
    translate({$coords.x}px,{$coords.y}px)  
    rotate({$coords.x * 0.2}deg)"  
>
</div>
```

In the code above, we created a div that listens to the `pannable` ‘s events. We use the `pannable` action with the `use:pannable` directive.

The `pannable` function has all the custom events that whatever it applies to emits.

In the `handleMousedown` listener of the `pannable` function, we have the `node` , which is the DOM node the action is attached to, and we set `x` and `y` with the mouse position. Then we call `dispatchEvent` to emit a custom event, which is `panstart` to send the `x` and `y` coordinates to our parent compone.t

In the `handleMousemove` function, we handle mouse moves by changing the position of the box, which is `node` , by using `x` and `y` to translate the box. Then we dispatch a custom `panmove` event to the parent.

When the mouse stops moving, the `handleMouseup` event handler is called, which sets `x` and `y` to the coordinates of the mouse. Then we dispatch the `panned` event with the `x` and `y` coordinates.

When the div box is destroyed, we called `node.removeListener` to remove the `mousedown` listener when the box is being destroyed.

In `App.svelte` , we use the `x` and `y` coordinates sent from `pannable` to update the coordinates of the box.

When also added a bounce effect with the `spring` transition that’s built into Svelte. `coords` is the animation effect that’s returned from `spring` . It has the `x` and `y` coordinates for the box, which we’ll update the location of the box as the mouse moves.

When the `panstart` , `panmove` , and `panned` events are triggered, we set the coordinates of the box.

Because `mousemove` triggers the emission of the `panstart` event we set the stiffness and the damping of the animation.

`mousemove` triggers the `panmove` event to be emitted, which changes the coordinates of the box.

Then when we stop moving the mouse and release the button, the `panend` event is triggered so that `handlePanEnd` is called to change the box’s coordinates back to (0, 0).

The `style` attribute of the div is dynamic and depends on `coords` , so the box will move as `coords` changes. We also rotate the box with the angle as a function of `coords` , so we’ll also see rotation.

Therefore, when we drag the box and then release the mouse button, we’ll see a bounce effect.

Conclusion
==========

In Svelte, actions are functions that emit custom events. We can attach the action to an element and then listen to those custom events to manipulate the elements as we wish.