Cours Svelte et SvelteKit · Module 10: Actions

Mjolnir - Actions

2 min de lecture ·
Dans cette leçon4

Bienvenue dans la forge de Mjolnir ! Le marteau de Thor possède des pouvoirs extraordinaires. Les Actions dans Svelte sont comme ces pouvoirs - elles étendent les capacités des éléments du DOM.

Que sont les Actions ?

Les Actions sont des fonctions appelées lorsqu'un élément est monté dans le DOM. En Svelte 5, tu places la configuration et le nettoyage dans un $effect à l'intérieur de l'action : la fonction renvoyée par $effect s'exécute quand l'élément disparaît :

<script>
  function myAction(node) {
    // node est l'élément du DOM, déjà présent dans le document
    console.log('Élément monté :', node);

    $effect(() => {
      // ici la configuration : écouteurs, bibliothèques

      return () => {
        // et ici le nettoyage, quand l'élément disparaît
        console.log('Élément démonté');
      };
    });
  }
</script>

<div use:myAction>
  Élément avec une action
</div>

Les actions ne s'exécutent pas pendant le rendu côté serveur (SSR) : elles ne démarrent que dans le navigateur.

L'API classique : un objet avec destroy

Avant l'arrivée de $effect, une action renvoyait un objet avec une méthode destroy() (et update(), vue dans la leçon suivante). Cette API fonctionne toujours et reste pratique dans les fichiers .js ordinaires : les runes comme $effect ne sont disponibles que dans les fichiers .svelte et .svelte.js.

// actions.js - un fichier JS ordinaire, sans runes
export function myAction(node) {
  console.log('Élément monté :', node);

  return {
    destroy() {
      console.log('Élément démonté');
    }
  };
}

Exemple : Clic à l'extérieur

// actions.js
export function clickOutside(node, callback) {
  function handleClick(event) {
    if (!node.contains(event.target)) {
      callback();
    }
  }

  document.addEventListener('click', handleClick, true);

  return {
    destroy() {
      document.removeEventListener('click', handleClick, true);
    }
  };
}
<script>
  import { clickOutside } from './actions.js';

  let isOpen = $state(false);
</script>

{#if isOpen}
  <div use:clickOutside={() => isOpen = false} class="dropdown">
    Clique en dehors de moi pour fermer
  </div>
{/if}

Actions avec paramètres

<script>
  function tooltip(node, text) {
    let tooltipEl;

    function show() {
      tooltipEl = document.createElement('div');
      tooltipEl.className = 'tooltip';
      tooltipEl.textContent = text;
      document.body.appendChild(tooltipEl);

      const rect = node.getBoundingClientRect();
      tooltipEl.style.left = rect.left + 'px';
      tooltipEl.style.top = rect.bottom + 5 + 'px';
    }

    function hide() {
      tooltipEl?.remove();
    }

    $effect(() => {
      node.addEventListener('mouseenter', show);
      node.addEventListener('mouseleave', hide);

      return () => {
        hide();
        node.removeEventListener('mouseenter', show);
        node.removeEventListener('mouseleave', hide);
      };
    });
  }
</script>

<button use:tooltip={'Voici un bouton'}>
  Survole-moi
</button>

Mjolnir donne du pouvoir - les Actions donnent du pouvoir aux éléments !

Code de cette leçon : App.svelte
<script>
  // Mjölnir - Pouvoirs assignés aux éléments
  // use:action - comportements DOM supplémentaires

  // Action simple - focus au montage
  function autofocus(node) {
    node.focus();

    // Nettoyage en Svelte 5 : la fonction renvoyée par $effect
    $effect(() => {
      return () => console.log('Élément supprimé');
    });
  }

  // Action avec paramètres
  function tooltip(node, text) {
    let tooltipEl;

    function show() {
      tooltipEl = document.createElement('div');
      tooltipEl.textContent = text;
      tooltipEl.className = 'tooltip';
      document.body.appendChild(tooltipEl);

      const rect = node.getBoundingClientRect();
      tooltipEl.style.left = rect.left + rect.width / 2 + 'px';
      tooltipEl.style.top = rect.top - 10 + 'px';
    }

    function hide() {
      if (tooltipEl) {
        tooltipEl.remove();
        tooltipEl = null;
      }
    }

    node.addEventListener('mouseenter', show);
    node.addEventListener('mouseleave', hide);

    // API classique des actions (toujours valable) : update et destroy
    return {
      // update - quand le paramètre change
      update(newText) {
        text = newText;
      },
      // nettoyage
      destroy() {
        hide();
        node.removeEventListener('mouseenter', show);
        node.removeEventListener('mouseleave', hide);
      }
    };
  }

  let tooltipText = $state('Je suis une infobulle !');
</script>

<main class="mjolnir">
  <h1>Mjölnir - Actions</h1>
  <p class="subtitle">use: - ajoute des pouvoirs aux éléments</p>

  <section>
    <h2>use:autofocus</h2>
    <input use:autofocus placeholder="Focus automatique au démarrage !" />
  </section>

  <section>
    <h2>use:tooltip</h2>
    <input
      bind:value={tooltipText}
      placeholder="Change le texte de l’infobulle"
    />
    <button use:tooltip={tooltipText}>
      Survole-moi !
    </button>
  </section>
</main>

<style>
  .mjolnir {
    font-family: 'Georgia', serif;
    max-width: 500px;
    margin: 0 auto;
    padding: 2rem;
    background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
    color: #e8e8f7;
    min-height: 100vh;
  }

  h1 {
    color: #7cb3f0;
    text-align: center;
  }

  .subtitle {
    text-align: center;
    color: #6a8ab9;
  }

  section {
    background: rgba(124, 179, 240, 0.1);
    border: 1px solid #7cb3f0;
    border-radius: 8px;
    padding: 1rem;
    margin: 1rem 0;
  }

  h2 {
    color: #7cb3f0;
    font-size: 1rem;
    margin-top: 0;
  }

  input {
    width: 100%;
    padding: 0.75rem;
    background: rgba(0,0,0,0.3);
    border: 1px solid #3a5f8a;
    border-radius: 4px;
    color: #e8e8f7;
    margin-bottom: 0.5rem;
  }

  input:focus {
    border-color: #7cb3f0;
    outline: none;
  }

  button {
    padding: 0.75rem 1.5rem;
    background: #3a5f8a;
    color: #e8e8f7;
    border: 1px solid #7cb3f0;
    border-radius: 4px;
    cursor: pointer;
  }

  :global(.tooltip) {
    position: fixed;
    background: #ffd700;
    color: #1a1a2e;
    padding: 0.5rem 1rem;
    border-radius: 4px;
    font-size: 0.9rem;
    transform: translate(-50%, -100%);
    z-index: 1000;
    pointer-events: none;
  }
</style>

Entrées de la documentation utilisées dans cette leçon

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Comment utilise-t-on une action sur un élément du DOM ?

  2. 2. Que peut retourner une fonction action ?

Articles utiles