Budi ingin menambahkan fitur “salin tautan pencarian” di toko bukunya: pengunjung yang mencari judul tertentu bisa membagikan URL hasil pencarian itu. Tautannya dibentuk dengan menggabungkan alamat dasar dan judul yang diketik pengunjung. Masalahnya muncul pada judul seperti “Kopi Susu & Gula Aren” — teks ini mengandung spasi dan tanda &, dua karakter yang di dalam URL punya tugas khusus. Ditempel mentah, tautan itu terbaca sebagai dua parameter, bukan satu judul.

Mengamati perilaku ini membawa kita ke aturan pentingnya: URL hanya mengizinkan sekelompok karakter tertentu, dan sisanya harus disandikan (encode) menjadi bentuk %XX sebelum boleh berada di dalam URL. Chapter ini membahas kapan menyandikan, fungsi mana yang dipakai, cara membacanya kembali, dan URLSearchParams yang mengerjakan semuanya secara otomatis.

Karakter yang Punya Tugas Khusus di URL #

Di dalam query string URL, beberapa karakter bukan sekadar teks:

  • ? menandai awal bagian query;
  • & memisahkan antar parameter;
  • = menghubungkan kunci dengan nilainya;
  • + dan %20 mewakili spasi;
  • # menandai awal bagian fragment.

Karena karakter-karakter ini punya tugas struktural, nilai parameter yang mengandungnya harus disandikan agar tafsirannya tidak berubah. Proses penyandian mengubah karakter bermasalah menjadi urutan % diikuti dua digit heksadesimal — misalnya & menjadi %26 dan spasi menjadi %20.

encodeURIComponent dan encodeURI #

Fungsi mana yang dipakai bergantung pada apa yang kita sandikan. Berkas berikut menguji keduanya pada kasus yang sama, lalu menutup dengan URLSearchParams:

// tautan-cari.js — menyandikan data agar aman berada di dalam URL
// --- 1. masalah: nilai berisi karakter khusus ---
const judul = "Kopi Susu & Gula Aren";
console.log("https://toko-budi.example.com/cari?judul=" + judul);

// --- 2. encodeURIComponent: untuk nilai parameter ---
console.log(encodeURIComponent(judul));
const aman = "https://toko-budi.example.com/cari?judul=" + encodeURIComponent(judul);
console.log(aman);

// --- 3. encodeURI: untuk URL utuh, struktur tetap hidup ---
console.log(encodeURI("https://toko-budi.example.com/cari?judul=Kopi Susu"));
console.log(encodeURIComponent("https://toko-budi.example.com"));

// --- 4. decode: kebalikan penyandian ---
console.log(decodeURIComponent(encodeURIComponent(judul)));
console.log(decodeURIComponent("Kopi+Susu"));

// --- 5. URLSearchParams menyandikan dan membaca otomatis ---
const params = new URLSearchParams();
params.set("judul", judul);
params.set("urutkan", "termurah");
console.log(params.toString());
console.log(params.get("judul"));

const dariTautan = new URLSearchParams("q=kopi+susu&stok=ada&stok=kosong");
console.log(dariTautan.get("q"));
console.log(dariTautan.getAll("stok"));

Output yang Dihasilkan:

text
https://toko-budi.example.com/cari?judul=Kopi Susu & Gula Aren
Kopi%20Susu%20%26%20Gula%20Aren
https://toko-budi.example.com/cari?judul=Kopi%20Susu%20%26%20Gula%20Aren
https://toko-budi.example.com/cari?judul=Kopi%20Susu
https%3A%2F%2Ftoko-budi.example.com
Kopi Susu & Gula Aren
Kopi+Susu
judul=Kopi+Susu+%26+Gula+Aren&urutkan=termurah
Kopi Susu & Gula Aren
kopi susu
[ 'ada', 'kosong' ]

Mari kita telaah baris-baris pentingnya:

  1. Baris 4 (tautan mentah): hasil penggabungan terlihat wajar di mata manusia, tetapi server membaca & sebagai pemisah parameter — parameter judul berhenti pada Kopi Susu , lalu muncul “parameter” baru bernama Gula Aren. Inilah bug yang nyaris selalu muncul tanpa penyandian.
  2. Baris 7 (encodeURIComponent(judul)): seluruh nilai disandikan — spasi menjadi %20 dan & menjadi %26. Nama fungsi ini menunjukkan tujuannya: menyandikan komponen URL, yaitu satu nilai utuh seperti isi parameter. Baris 8–9 memperlihatkan tautan yang sah setelah nilai disandikan.
  3. Baris 12 (encodeURI(...)): untuk URL utuh, encodeURI menyandikan spasi tetapi membiarkan ?, &, dan = tetap hidup — struktur tautannya tidak rusak. Inilah bedanya dengan baris 13: saat seluruh URL diberikan ke encodeURIComponent, sampai :// ikut disandikan menjadi https%3A%2F%2F... — teks itu sudah bukan alamat yang bisa dibuka.
  4. Baris 16–17 (decode): decodeURIComponent adalah kebalikan persisnya — nilai yang disandikan kembali menjadi teks asli. Baris 17 menguji batasannya: tanda + tidak diubah menjadi spasi, karena bagi decodeURIComponent, + adalah karakter biasa.
  5. Baris 20–24 (URLSearchParams): method set menyimpan kunci-nilai, toString menghasilkan query string yang sudah tersandikan — spasi menjadi + (bentuk lain yang sah untuk nilai parameter) dan & menjadi %26. Method get membaca kembali nilai aslinya tanpa kita menyentuh fungsi decode sama sekali.
  6. Baris 26–28 (membaca query string): constructor menerima teks query mentah dan mengurainya. get("q") mengembalikan kopi susu — + otomatis ditafsirkan spasi — dan getAll("stok") mengembalikan kedua nilai untuk kunci yang muncul dua kali.

Perbedaan arah kedua fungsi itu paling jelas bila string yang sama diberikan ke keduanya:

js
console.log(encodeURI("cari?judul=Kopi & Gula"));
console.log(encodeURIComponent("cari?judul=Kopi & Gula"));

Output yang Dihasilkan:

text
cari?judul=Kopi%20&%20Gula
cari%3Fjudul%3DKopi%20%26%20Gula

encodeURI menyandikan hanya spasi — ? dan & tetap hidup karena dianggap struktur. encodeURIComponent menyandikan semuanya: ? menjadi %3F, = menjadi %3D, dan & menjadi %26. Dari sini aturannya mudah diingat: struktur dibaca dengan encodeURI, isi dibungkus dengan encodeURIComponent.

Membaca Kembali dan Batasan Decode #

Fungsi decode punya satu cara gagal yang perlu dikenali: teks persen yang tidak lengkap atau tidak valid. decodeURIComponent tidak menebak — ia melempar galat:

js
try {
  decodeURIComponent("%E0%A4%A");
} catch (galat) {
  console.log(galat.name + ": " + galat.message);
}

Output yang Dihasilkan:

text
URIError: URI malformed

Urutan %E0%A4%A hanya punya tiga digit heksadesimal setelah %E0, sehingga tidak bisa diurai sebagai karakter yang sah. Pesan URIError: URI malformed seperti ini lazim muncul saat teks query mengandung % yang bukan bagian penyandian — misalnya judul “Diskon 50%” yang disandikan sebagian. Bila sumber teksnya tidak terkendali, panggilan decode sebaiknya dilindungi try...catch.

Satu contoh terakhir yang sering dipakai: alamat email sebagai nilai parameter. Karakter @ termasuk yang disandikan:

js
console.log(encodeURIComponent("[email protected]"));

Output yang Dihasilkan:

text
budi%40example.com

Hasilnya budi%40example.com — @ berubah menjadi %40. Bentuk seperti inilah yang tampak di kolom alamat browser ketika email dikirim lewat URL.

Penyandian yang sama berlaku bila nilai berpotensi mengandung / tetapi dipakai sebagai satu bagian alamat (path segment) — misalnya nomor dokumen yang bergaya data/2026. Tanpa penyandian, garis miring itu akan dibaca sebagai pembatas folder; dengan penyandian, seluruh nilai tetap satu segmen:

js
console.log(encodeURIComponent("data/2026/laporan"));

Output yang Dihasilkan:

text
data%2F2026%2Flaporan

Hasilnya data%2F2026%2Flaporan — setiap / berubah menjadi %2F, sehingga nilai yang mengandung garis miring pun aman berdiri sendiri sebagai satu segmen alamat.

URLSearchParams Menangani Semuanya #

Setelah melihat manualnya, pola yang disarankan jelas: biarkan URLSearchParams menyandikan dan mengurai. Kita hanya menyebut kunci dan nilai aslinya — params.set("judul", judul) — dan query string yang dihasilkan toString() sudah pasti sah. Bacaannya pun simetris: get, getAll, dan has mengembalikan nilai asli, bukan bentuk tersandikan.

Perbedaan + dan %20 juga kini utuh: keduanya sah sebagai spasi di dalam nilai parameter, dan URLSearchParams menafsirkan keduanya sama saat membaca — tetapi decodeURIComponent hanya menerima %20, karena bagi fungsi itu + hanyalah karakter biasa. Isi URLSearchParams sendiri bisa ditelusuri seperti koleksi lain yang sudah kita kenal — pasangan kunci-nilainya keluar berurutan:

js
const params = new URLSearchParams("q=kopi&urutkan=termurah");
for (const [kunci, nilai] of params) {
  console.log(kunci + " = " + nilai);
}

Output yang Dihasilkan:

text
q = kopi
urutkan = termurah

Dengan data yang sudah aman disandikan dan dibaca kembali, bab terakhir section ini mengumpulkan semuanya: menerima data dari luar, memeriksa bentuknya, dan mengolahnya dengan aman.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

tautan-cari.js

Unduh berkas
javascript
// tautan-cari.js — menyandikan data agar aman berada di dalam URL
// --- 1. masalah: nilai berisi karakter khusus ---
const judul = "Kopi Susu & Gula Aren";
console.log("https://toko-budi.example.com/cari?judul=" + judul);

// --- 2. encodeURIComponent: untuk nilai parameter ---
console.log(encodeURIComponent(judul));
const aman = "https://toko-budi.example.com/cari?judul=" + encodeURIComponent(judul);
console.log(aman);

// --- 3. encodeURI: untuk URL utuh, struktur tetap hidup ---
console.log(encodeURI("https://toko-budi.example.com/cari?judul=Kopi Susu"));
console.log(encodeURIComponent("https://toko-budi.example.com"));

// --- 4. decode: kebalikan penyandian ---
console.log(decodeURIComponent(encodeURIComponent(judul)));
console.log(decodeURIComponent("Kopi+Susu"));

// --- 5. URLSearchParams menyandikan dan membaca otomatis ---
const params = new URLSearchParams();
params.set("judul", judul);
params.set("urutkan", "termurah");
console.log(params.toString());
console.log(params.get("judul"));

const dariTautan = new URLSearchParams("q=kopi+susu&stok=ada&stok=kosong");
console.log(dariTautan.get("q"));
console.log(dariTautan.getAll("stok"));