Setiap pengunjung halaman promo toko Budi diminta memakai kode DISKON20 di checkout. Cara manualnya merepotkan: pengunjung harus menyeleksi teks dengan tepat, menyalin, lalu berpindah kolom — dan seleksi yang meleset satu huruf membuat kode ditolak. Bagi halaman web yang menerima izin, browser menyediakan jalur yang lebih baik: Clipboard API, yang bisa menulis teks ke clipboard dan membacanya kembali secara terprogram — semuanya asinkron dan diawasi sistem izin.
Antarmukanya berada di navigator.clipboard, dengan dua method utama: writeText() untuk menulis dan readText() untuk membaca. Keduanya mengembalikan promise, sehingga dipakai dengan await seperti pekerjaan asinkron lain yang sudah kita kenal.
Aturan mainnya bisa dirangkum tiga kalimat. Pertama, clipboard bersifat global — isinya dibagi dengan seluruh aplikasi di perangkat, bukan hanya halaman kita — karena itu semua akses diawasi sistem izin. Kedua, operasinya asinkron: browser menunggu izin dan proses penyimpanan selesai, lalu memberi jawaban lewat promise. Ketiga, keberadaannya terikat secure context: halaman yang tidak aman tidak mendapatkan objeknya sama sekali.
Menulis ke Clipboard dengan writeText #
navigator.clipboard.writeText(teks) menyimpan string ke clipboard sistem dan menyelesaikan promise-nya setelah penyimpanan berhasil. Pembatalan oleh pengguna atau kegagalan izin membuat promise ditolak dengan Error — karena itu pola try-catch menyertai setiap pemanggilan:
// clipboard-promo.js — menyalin dan menempel kode promo lewat Clipboard API
// API navigator.clipboard hanya ada di browser (secure context);
// berkas ini lolos node --check dan dijalankan pada halaman toko.
const tombolSalin = document.querySelector("#salin-kupon");
const tombolTempel = document.querySelector("#tempel-kupon");
// --- 1. menulis teks ke clipboard ---
tombolSalin.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText("DISKON20");
console.log("Kode DISKON20 tersalin ke clipboard.");
} catch (error) {
console.log("Gagal menyalin:", error.name, "-", error.message);
}
});
// --- 2. membaca teks dari clipboard (butuh izin clipboard-read) ---
tombolTempel.addEventListener("click", async () => {
try {
const teks = await navigator.clipboard.readText();
console.log("Dari clipboard:", teks.trim());
} catch (error) {
console.log("Baca clipboard ditolak:", error.name);
}
});Clipboard API tidak tersedia di Node maupun di lingkungan jsdom, sehingga berkas ini diverifikasi sintaksnya dengan node --check clipboard-promo.js, sedangkan keluarannya kita catat sebagai keluaran console browser berdasarkan perilaku yang dispesifikasikan standar. Setelah pengunjung menekan tombol #salin-kupon, console mencatat:
Keluaran console browser:
Kode DISKON20 tersalin ke clipboard.Mari kita telaah baris-baris pentingnya:
- Baris 9 (
addEventListener("click", async ...)): penulisan clipboard dipasangkan dengan aksi pengguna (klik tombol). Browser mengizinkanwriteTextpada saat klik tanpa dialog izin tambahan — izinclipboard-writedianggap terpenuhi untuk gestur pengguna. - Baris 11 (
await navigator.clipboard.writeText(...)): teksDISKON20dikirim apa adanya; tidak ada pemformatan atau seleksi yang bisa meleset. Promise selesai setelah clipboard benar-benar terisi. - Baris 13–15 (
catch): kegagalan diberi nama yang informatif —NotAllowedErrorbila izin atau konteks tidak memungkinkan. Pesan di console jauh lebih berguna daripada tombol yang diam saja. - Baris 21–22 (
readTextdantrim()): isi clipboard dibaca sebagai string.trim()menghapus spasi atau baris baru yang sering ikut tersalin, sehinggaDISKON20\ntetap diterima checkout sebagai kode yang valid.
Secure Context dan Deteksi Ketersediaan #
Syarat secure context bukan formalitas teknis semata. Isi clipboard milik pengunjung bisa berupa data apa pun — alamat, teks pribadi, bahkan data dari aplikasi lain — dan menyalurkannya lewat koneksi tanpa enkripsi berarti membiarkan pihak di tengah jaringan membacanya. Karena itu browser menetapkan ambang: pada halaman HTTPS dan localhost, navigator.clipboard tersedia; pada HTTP biasa properti itu tidak ada.
Konsekuensi praktisnya ada di urutan pemeriksaan. Sebelum memanggil writeText atau readText, halaman memeriksa keberadaan objeknya:
if (navigator.clipboard) {
console.log("Clipboard API tersedia.");
} else {
console.log("Tidak ada clipboard: pakai jalur manual.");
}Pemeriksaan sederhana ini mencegah TypeError pada halaman HTTP, sekaligus menjadi titik bercabang yang sama dengan pola degrade gracefully yang kita bahas di chapter penutup section ini. Catatan tambahan: ketersediaan objek tidak menjamin operasinya berhasil — izin bisa ditolak, dan penyalinan yang tidak berlangsung di dalam aksi pengguna dapat diblokir browser. Keduanya dilaporkan lewat promise yang ditolak, dan catch di berkas utama sudah menampungnya.
Membaca dari Clipboard dengan readText #
Membaca lebih sensitif daripada menulis: isi clipboard bisa berisi apa saja yang pernah disalin pengunjung — termasuk data dari aplikasi lain. Karena itu readText() memicu permintaan izin clipboard-read. Saat izin masih berstatus prompt, browser menampilkan dialog; pengunjung yang menolak membuat promise ditolak dengan NotAllowedError, dan baris catch di atas mencatat:
Keluaran console browser:
Dari clipboard: DISKON20atau, bila izin ditolak:
Baca clipboard ditolak: NotAllowedErrorPerlu ditegaskan bahwa readText() hanya mengembalikan teks polos. Bila pengunjung menyalin gambar atau berkas dari aplikasi lain, hasil yang terbaca adalah string kosong atau teks pengantinya — bukan isi berkasnya. Untuk kebutuhan membaca data non-teks, API menyediakan method read() yang bekerja pada objek ClipboardItem, dan bahasan lengkapnya keluar dari lingkup chapter ini; pola readText sudah menutup kasus teks seperti kode promo dan tautan.
Dari sisi penerapan, pembacaan clipboard paling tepat ditempel pada aksi yang eksplisit: tombol “Tempel kode” di kolom voucher. Dengan begitu dialog izin muncul ketika pengunjung jelas-jelas memintanya — bukan saat halaman dibuka — dan hasilnya langsung dipakai untuk mengisi kolom, bukan sekadar dicatat di console.
Fallback untuk Browser Tua #
Untuk halaman yang masih harus mendukung browser tanpa Clipboard API, fallback standarnya adalah trik seleksi dengan document.execCommand("copy") — method lama yang kini resmi ditinggalkan (deprecated), tetapi masih berfungsi sebagai jaring pengaman:
function salinCaraTua(teks) {
const area = document.createElement("textarea");
area.value = teks;
document.body.appendChild(area);
area.select();
const berhasil = document.execCommand("copy");
area.remove();
return berhasil;
}Polanya: buat textarea sementara berisi teks, seleksi isinya, jalankan execCommand("copy"), lalu bersihkan. Method ini bersifat sinkron dan hanya boleh dipanggil di dalam handler aksi pengguna. Nilai kembaliannya boolean — true berarti browser melaksanakan penyalinan. Dua batasannya perlu diingat: hasilnya selalu teks polos, dan seleksi yang dilakukannya dapat menggeser seleksi aktif pengunjung — itulah sebabnya textarea sementara dibuat, dipakai, lalu dibuang.
Bila navigator.clipboard tersedia, gunakan API modern; execCommand cukup dijadikan jalur cadangan dengan if (!navigator.clipboard). Dengan demikian kedua jalur salin — modern dan lama — hidup berdampingan dalam satu function yang sama, dan pemilihannya otomatis mengikuti kemampuan browser.
Dengan dua method dan satu fallback ini, pola “salin kode promo”, “tempel dari clipboard”, dan kompatibilitas browser lama sudah tertutup semua — sisanya adalah pemeriksaan izin yang rapi, yang menjadi topik chapter penutup section ini.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
clipboard-promo.js
Unduh berkas// clipboard-promo.js — menyalin dan menempel kode promo lewat Clipboard API
// API navigator.clipboard hanya ada di browser (secure context);
// berkas ini lolos node --check dan dijalankan pada halaman toko.
const tombolSalin = document.querySelector("#salin-kupon");
const tombolTempel = document.querySelector("#tempel-kupon");
// --- 1. menulis teks ke clipboard ---
tombolSalin.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText("DISKON20");
console.log("Kode DISKON20 tersalin ke clipboard.");
} catch (error) {
console.log("Gagal menyalin:", error.name, "-", error.message);
}
});
// --- 2. membaca teks dari clipboard (butuh izin clipboard-read) ---
tombolTempel.addEventListener("click", async () => {
try {
const teks = await navigator.clipboard.readText();
console.log("Dari clipboard:", teks.trim());
} catch (error) {
console.log("Baca clipboard ditolak:", error.name);
}
});