Kurs Svelte i SvelteKit · Moduł 10: Akcje

Mjölnir - Actions

2 min czytania ·
W tej lekcji4

Witaj w kuźni Mjölnira! Młot Thora ma niezwykłe moce. Actions w Svelte to właśnie takie moce - rozszerzają możliwości elementów DOM.

Czym są Actions?

Actions to funkcje wywoływane, gdy element zostaje zamontowany w DOM. W Svelte 5 konfigurację i sprzątanie umieszczasz w $effect wewnątrz akcji - funkcja zwrócona z $effect uruchomi się, gdy element zniknie:

<script>
  function myAction(node) {
    // node to element DOM - już jest w dokumencie
    console.log('Element zamontowany:', node);

    $effect(() => {
      // tu konfiguracja: listenery, biblioteki

      return () => {
        // a tu sprzątanie, gdy element znika
        console.log('Element odmontowany');
      };
    });
  }
</script>

<div use:myAction>
  Element z akcją
</div>

Akcje nie działają podczas renderowania na serwerze (SSR) - uruchamiają się dopiero w przeglądarce.

Klasyczne API: obiekt z destroy

Zanim pojawił się $effect, akcja zwracała obiekt z metodą destroy() (i update(), o której w następnej lekcji). To API nadal działa i przydaje się w zwykłych plikach .js - runy, takie jak $effect, są dostępne tylko w plikach .svelte i .svelte.js.

// actions.js - zwykły plik JS, bez runów
export function myAction(node) {
  console.log('Element zamontowany:', node);

  return {
    destroy() {
      console.log('Element odmontowany');
    }
  };
}

Przykład: Click Outside

// 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">
    Kliknij poza mną żeby zamknąć
  </div>
{/if}

Akcje z parametrami

<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={'To jest przycisk'}>
  Najedź na mnie
</button>

Mjölnir daje moc - Actions dają moc elementom!

Kod do tej lekcji: App.svelte
<script>
  // Mjölnir - Moce przypisane do elementów
  // use:action - dodatkowe zachowania DOM

  // Prosta akcja - focus na mount
  function autofocus(node) {
    node.focus();

    // Sprzątanie w Svelte 5: funkcja zwrócona z $effect
    $effect(() => {
      return () => console.log('Element usunięty');
    });
  }

  // Akcja z parametrami
  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);

    // Klasyczne API akcji (nadal działa): update i destroy
    return {
      // update - gdy parametr się zmieni
      update(newText) {
        text = newText;
      },
      // cleanup
      destroy() {
        hide();
        node.removeEventListener('mouseenter', show);
        node.removeEventListener('mouseleave', hide);
      }
    };
  }

  let tooltipText = $state('Jestem tooltipem!');
</script>

<main class="mjolnir">
  <h1>Mjölnir - Actions</h1>
  <p class="subtitle">use: - dodaj moce do elementów</p>

  <section>
    <h2>use:autofocus</h2>
    <input use:autofocus placeholder="Auto-fokus na start!" />
  </section>

  <section>
    <h2>use:tooltip</h2>
    <input
      bind:value={tooltipText}
      placeholder="Zmień tekst tooltipa"
    />
    <button use:tooltip={tooltipText}>
      Najedź na mnie!
    </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>

Hasła z dokumentacji w tej lekcji

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Jak używamy action na elemencie DOM?

  2. 2. Co może zwrócić funkcja action?

Przydatne artykuły