Action adalah function biasa yang Svelte panggil saat sebuah elemen masuk ke DOM. Kamu menempelkannya ke elemen dengan use:namaAction, dan function itu menerima elemennya langsung, jadi dia bisa memberi fokus, memasang atribut, atau mendengarkan klik. Bayangin penjepit bon di Warung Bu Sri. Penjepit yang sama bisa dipasang ke piring mana saja: saat dipasang, bon dijepit; kalau pesanan berubah, kertas bonnya diganti; saat piring diangkat, penjepitnya dilepas supaya gak tercecer di meja. Piring itu elemennya, bon itu parameternya, dan tiga momen itu adalah pasang, update, dan destroy.
🧪 Tebakan cepat: action
klikDiLuarmenutup dropdown keranjang saat pembeli mengklik di luarnya, tapi lupa diberidestroy. Dropdown dibuka, lalu ditutup dengan klik di luar. Dibuka lagi, lalu pembeli mengklik baris Nasi Goreng di dalamnya. Dropdown-nya tetap terbuka, atau malah tertutup? Jawabannya ada di bagian "destroy: lepas yang kamu pasang".
Action pertama: autofocus
<script>
function autofocus(node) {
node.focus()
}
</script>
<input use:autofocus placeholder="Cari menu..." />
use:autofocusmenyuruh Svelte memanggilautofocus(node)sekali, tepat setelah<input>itu masuk ke DOM.nodeadalah elemen<input>itu sendiri.- Function-nya biasa saja, gak diimpor dari
"svelte". Boleh ditulis di file yang sama, atau diactions.jslalu diimpor ke banyak komponen. - Kenapa gak memanggil
focus()langsung di<script>? Dari pelajaran Lifecycle: saat script jalan, elemennya belum ada. Action jalan setelah elemennya ada, miriponMount, tapi untuk satu elemen tertentu. - Di SvelteKit, action cuma jalan di browser, gak ikut jalan saat halaman dirender di server.
Latihan pertama persis ini: kode awalnya sudah punya function autofocus, kamu tinggal memasang use:autofocus di <input>.
Parameter dan update: tooltip total
Action boleh menerima satu nilai: tulis use:tooltip={nilai}, dan nilai itu jadi argumen kedua. Kalau action mengembalikan objek berisi update dan destroy, Svelte memanggil update tiap nilainya berubah, dan destroy saat elemennya dilepas.
<script>
let porsiEsTeh = 3
$: total = 2 * 15000 + 12000 + porsiEsTeh * 5000
function tooltip(node, teks) {
node.setAttribute("title", teks)
return {
update(teksBaru) {
node.setAttribute("title", teksBaru)
},
destroy() {
node.removeAttribute("title")
},
}
}
</script>
<button use:tooltip={`Total Rp ${total.toLocaleString("id-ID")}`}>Keranjang (3)</button>
<button on:click={() => (porsiEsTeh += 1)}>+1 Es Teh</button>
Keranjangnya sama dengan pelajaran Animations: Nasi Goreng x2, Mie Ayam x1, Es Teh x3. Ini yang Svelte 4 panggil:
| Kejadian | Yang dipanggil | title tombol Keranjang |
|---|---|---|
| tombol masuk ke DOM | tooltip(button, "Total Rp 57.000") |
Total Rp 57.000 |
| klik +1 Es Teh | update("Total Rp 62.000") |
Total Rp 62.000 |
| variabel lain berubah | gak ada | tetap |
| tombol dilepas | destroy() |
atribut dihapus |
node.setAttribute("title", teks)memasang atributtitle, yang browser tampilkan sebagai tooltip saat kursor diam di atas tombol.node.title = tekshasilnya sama.updatecuma dipanggil kalau nilai parameternya berubah. Variabel yang gak dipakai di parameter boleh berubah sesukanya tanpa memanggilupdate.- Parameternya cuma satu. Butuh lebih dari satu nilai? Kirim objek:
use:tooltip={{ teks, posisi: "atas" }}.
Latihan kedua membuat action ini sendiri: tooltip(node, text) dengan setAttribute('title', text), dipasang dengan use:tooltip={'Ini tooltip'} di <button>.
klikDiLuar: menutup dropdown keranjang
<script>
let buka = false
function klikDiLuar(node, tutup) {
function cek(event) {
if (!node.contains(event.target)) tutup()
}
// true: dengarkan di fase capture (penjelasannya di bawah)
document.addEventListener("click", cek, true)
return {
update(tutupBaru) {
tutup = tutupBaru
},
destroy() {
document.removeEventListener("click", cek, true)
},
}
}
</script>
<button on:click={() => (buka = true)}>Keranjang (3)</button>
{#if buka}
<div class="dropdown" use:klikDiLuar={() => (buka = false)}>
<p>Nasi Goreng x2</p>
<p>Mie Ayam x1</p>
<p>Es Teh x3</p>
</div>
{/if}
- Tiap
bukajaditrue,{#if}memasang dropdown baru, danklikDiLuardipanggil untuknya. Tiapbukajadifalse, dropdown dilepas, dandestroyjalan. cekmendengarkan semua klik di halaman.node.contains(event.target)bernilaitruekalau yang diklik ada di dalam dropdown. Selain itu,tutup()dipanggil.- Parameternya kali ini function.
updatemenyimpan function terbaru ketutup, supayacekselalu memanggil versi yang sekarang. - Kenapa
true? Klik merambat dari elemen yang diklik naik ke induk-induknya sampaidocument; ini disebut bubbling. Fase capture adalah perjalanan sebaliknya, daridocumentturun ke elemen itu, dan terjadi lebih dulu. Klik yang membuka dropdown masih merambat naik saat listener dipasang. Tanpatrue, listener baru itu ikut menerima klik tombol Keranjang, menganggapnya klik di luar, dan dropdown langsung tertutup lagi. Di fase capture, klik itu sudah lewat, jadi aman.
destroy: lepas yang kamu pasang
Sekarang destroy dihapus dari klikDiLuar. Ini yang terjadi, dicoba di browser dengan Svelte 4:
| Kejadian | Dengan destroy |
Tanpa destroy |
|---|---|---|
| buka | 1 listener | 1 listener |
| klik di luar, dropdown tertutup | 0 listener | 1 listener, memegang dropdown lama |
| buka lagi | 1 listener | 2 listener |
| klik Nasi Goreng di dalam dropdown | tetap terbuka | tertutup |
Listener pertama gak pernah dilepas, dan dia masih memegang node dropdown pertama, yang sudah dibuang dari halaman. Dropdown yang sudah dibuang gak berisi apa pun yang ada di halaman, jadi node.contains(event.target) selalu false, dan listener lama itu menganggap tiap klik sebagai klik di luar. Klik di Nasi Goreng pun memanggil tutup(). Jadi dropdown-nya tertutup. Itu jawaban tebakannya.
Masalahnya juga menumpuk: setelah tiga kali buka-tutup, tiap klik di mana pun menjalankan tiga cek sekaligus. Ini memory leak yang sama dengan interval yang lupa dihentikan di pelajaran Lifecycle. Aturannya: apa pun yang action pasang di luar elemennya sendiri (listener di document atau window, interval, observer) harus dilepas di destroy.
Svelte 5
use: jalan sama persis di Svelte 5, juga di komponen yang memakai runes. Svelte 5.29 menambah cara yang lebih baru, {@attach ...}: function-nya menerima elemen, mengembalikan function pembersih, dan dijalankan ulang otomatis saat state yang dibacanya berubah, jadi gak perlu update. Latihannya di-compile dengan Svelte 4, jadi tulis use:.
⚠️ Jebakan yang sering ketemu
- Lupa
destroy. Listener didocumenttetap hidup setelah elemennya hilang, menumpuk tiap kali elemennya dipasang lagi, dan bisa menutup dropdown yang baru dibuka. - Listener
documenttanpatrue. Klik yang membuka dropdown ikut sampai ke listener barunya, jadi dropdown langsung tertutup lagi. use:di komponen.<MenuItem use:tooltip={...} />ditolak compiler:Actions can only be applied to DOM elements, not components. Pasang di elemen HTML di dalam komponennya.- Yang dicek latihannya. Latihan pertama: ada
<input>, kodemu memakaiuse:, dan input-nya benar-benar fokus. Latihan kedua: ada<button>, kodemu memakaiuse:tooltip, adasetAttribute("title"atau.title =, dan tombolnya benar-benar punyatitle="Ini tooltip".
TL;DR: Action adalah function namaAction(node, parameter) yang dipasang dengan use:namaAction={parameter} dan dipanggil sekali saat elemennya masuk ke DOM, jadi aman memakai DOM di dalamnya: node.focus(), node.setAttribute("title", teks), atau listener klik. Action boleh mengembalikan { update, destroy }: update(nilaiBaru) jalan saat parameternya berubah, destroy() jalan saat elemennya dilepas. Semua yang dipasang di luar elemennya harus dilepas di destroy; tanpa itu, listener klikDiLuar menumpuk dan menutup dropdown yang baru dibuka. Listener di document untuk klik di luar pakai fase capture (true), supaya klik pembukanya gak langsung menutupnya. Action cuma untuk elemen HTML, bukan komponen. Di Svelte 5, use: tetap jalan, dan {@attach} adalah alternatif yang lebih baru. Latihannya: <input use:autofocus />, lalu <button use:tooltip={'Ini tooltip'}>Hover saya</button>.