Halaman toko Budi kini memiliki beberapa fitur yang masing-masing meminta izin: salin kode promo (clipboard), cari cabang terdekat (geolocation), dan pengingat stok (notifications). Menampilkan ketiga dialog izin sekaligus saat halaman dibuka adalah resep penolakan massal — pengunjung yang belum paham konteks akan menolak semuanya, dan penolakan yang tersimpan membuat fitur tidak pernah berfungsi kembali. Cara yang lebih baik: tanyakan izin saat fiturnya benar-benar dipakai, dan ketahui lebih dulu apa status izin yang sudah tersimpan.
Permissions API menyediakan pembacaan itu lewat navigator.permissions.query() — satu antarmuka untuk melihat status izin semua API berizin: granted, denied, atau prompt. Status ini tidak diberikan lewat Permissions API sendiri; ia hanya melaporkan, lalu pemberian izin tetap terjadi pada API masing-masing seperti yang sudah kita lihat pada readText() dan getCurrentPosition().
Membaca Status dengan permissions.query #
Aturannya: navigator.permissions.query({ name: namaIzin }) mengembalikan promise yang terisi object PermissionStatus — dengan properti state berisi status saat ini dan listener untuk perubahannya. Berkas berikut merangkai pemeriksaan izin dengan fitur salin promo:
// cek-izin.js — memeriksa status izin sebelum memakai API browser
// Permissions API hanya ada di browser;
// berkas ini lolos node --check dan dijalankan pada halaman toko.
// --- 1. membaca status izin sebuah API ---
async function periksaIzin(nama) {
try {
const status = await navigator.permissions.query({ name: nama });
console.log(`${nama}: ${status.state}`);
return status;
} catch (error) {
console.log(`${nama}: query tidak didukung (${error.name})`);
return null;
}
}
// --- 2. pola degrade gracefully untuk fitur salin ---
async function siapkanFiturSalin() {
if (!navigator.clipboard) {
console.log("Tanpa clipboard: tampilkan kode manual DISKON20.");
return;
}
const status = await periksaIzin("clipboard-write");
if (status === null) return;
if (status.state === "granted") {
console.log("Tombol salin siap: izin sudah diberikan.");
} else if (status.state === "denied") {
console.log("Izin ditolak: tampilkan kode manual DISKON20.");
} else {
console.log("Izin prompt: tombol salin tetap aktif, dialog muncul saat ditekan.");
}
// perubahan status (misalnya pengguna mengubah pengaturan situs)
status.addEventListener("change", () => {
console.log(`Status clipboard-write berubah: ${status.state}`);
});
}
siapkanFiturSalin();Permissions API tidak tersedia di Node maupun jsdom, sehingga berkas ini diverifikasi sintaksnya dengan node --check cek-izin.js; keluaran berikut dicatat sebagai keluaran console browser sesuai perilaku standar. Pada kunjungan pertama — izin belum pernah diminta — console mencatat:
Keluaran console browser:
clipboard-write: prompt
Izin prompt: tombol salin tetap aktif, dialog muncul saat ditekan.Setelah pengunjung memberi izin dan halaman dibuka lagi, status tersimpan dan deretannya berubah menjadi:
Keluaran console browser:
clipboard-write: granted
Tombol salin siap: izin sudah diberikan.Mari kita telaah baris-baris pentingnya:
- Baris 6–15 (
function periksaIzin):query()dipanggil dengan object{ name: nama }. Promise-nya selesai denganPermissionStatus; kegagalan ditangkaptry-catchkarena nama izin yang tidak dikenal melemparTypeError— halaman tidak boleh berhenti hanya karena satu query gagal. - Baris 9 (
status.state): string berisigranted,denied, atauprompt. Nilai inilah seluruh jawaban yang kita butuhkan untuk menentukan jalur UI. - Baris 19–22 (pemeriksaan keberadaan API): sebelum urusan izin, fitur diperiksa keberadaannya. Browser tanpa
navigator.clipboardtidak akan pernah bisa menyalin — jalur manual menjadi satu-satunya pilihan, tanpa memanggilquerysama sekali. - Baris 27–33 (tiga cabang status):
grantedberarti tombol siap dipakai tanpa dialog;deniedberarti jangan meminta lagi — tampilkan jalur manual;promptberarti tombol tetap aktif, dan dialog izin baru muncul ketika pengunjung benar-benar menekannya. - Baris 36–38 (
addEventListener("change", ...)): status izin bukan nilai statis. Pengunjung dapat membuka pengaturan situs lalu membalik izinnya; saat itu terjadi, listener ini dijalankan dan UI bisa disesuaikan tanpa memuat ulang halaman.
Arti Tiga Status #
Setiap status menuntut sikap yang berbeda dari halaman kita:
| Status | Arti | Sikap halaman |
|---|---|---|
granted | izin sudah diberikan | fitur aktif penuh; pemanggilan API tidak memunculkan dialog |
denied | izin ditolak dan tersimpan | jangan meminta lagi; sediakan jalur alternatif manual |
prompt | belum pernah diminta | fitur tetap aktif; dialog muncul saat API dipanggil lewat aksi pengguna |
Status denied paling perlu dihormati: browser menyimpan penolakan itu, dan dialog izin tidak akan ditampilkan lagi oleh pemanggilan biasa. Halaman yang terus mencoba memakai API yang ditolak hanya mengumpulkan galat di console. Satu-satunya jalan kembali adalah pengunjung membuka pengaturan situs sendiri — karena itu listener change berharga: begitu pengunjung membalik statusnya, halaman bisa langsung menyesuaikan diri.
Nama izin yang umum diperiksa mengikuti API-nya: "geolocation", "clipboard-read", "clipboard-write", "notifications", "camera", dan "microphone". Tidak semua nama didukung semua browser — beberapa izin bahkan tidak punya status yang bisa dibaca — dan itulah alasan periksaIzin selalu mengembalikan null pada kegagalan, bukan melempar galat ke pemanggil.
Pola Degrade Gracefully #
Rangkaian di cek-izin.js mengikuti pola yang berlaku untuk semua API berizin — dan sebagian besar API browser pada umumnya. Pola ini disebut degrade gracefully: fitur tetap berfungsi sebagaimana kemampuan lingkungannya, dan bagian yang tidak tersedia digantikan alternatif, bukan menggagalkan seluruh halaman. Empat langkahnya:
- Periksa keberadaan API (
if (!navigator.clipboard)): lingkungan tidak mendukung sama sekali → jalur manual. - Periksa status izin (
permissions.query): lingkungan mendukung, statusdenied→ jalur manual;granted→ langsung pakai. - Minta lewat aksi pengguna: status
prompt→ fitur tetap ditayangkan; dialog izin muncul tepat saat pengunjung menekan tombolnya. - Dengarkan perubahan (
change): status bisa berbalik kapan saja; sesuaikan UI tanpa memuat ulang.
Urutan langkah itu juga menjelaskan letak Permissions API di section ini: ia bekerja di atas semua API berizin yang sudah kita pelajari — clipboard, geolocation, dan notifications. Dengan satu function periksaIzin, halaman toko Budi kini tahu kapan boleh langsung beraksi, kapan harus menunggu dialog, dan kapan sebaiknya menyerahkan pilihan sepenuhnya kepada pengunjung.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
cek-izin.js
Unduh berkas// cek-izin.js — memeriksa status izin sebelum memakai API browser
// Permissions API hanya ada di browser;
// berkas ini lolos node --check dan dijalankan pada halaman toko.
// --- 1. membaca status izin sebuah API ---
async function periksaIzin(nama) {
try {
const status = await navigator.permissions.query({ name: nama });
console.log(`${nama}: ${status.state}`);
return status;
} catch (error) {
console.log(`${nama}: query tidak didukung (${error.name})`);
return null;
}
}
// --- 2. pola degrade gracefully untuk fitur salin ---
async function siapkanFiturSalin() {
if (!navigator.clipboard) {
console.log("Tanpa clipboard: tampilkan kode manual DISKON20.");
return;
}
const status = await periksaIzin("clipboard-write");
if (status === null) return;
if (status.state === "granted") {
console.log("Tombol salin siap: izin sudah diberikan.");
} else if (status.state === "denied") {
console.log("Izin ditolak: tampilkan kode manual DISKON20.");
} else {
console.log("Izin prompt: tombol salin tetap aktif, dialog muncul saat ditekan.");
}
// perubahan status (misalnya pengguna mengubah pengaturan situs)
status.addEventListener("change", () => {
console.log(`Status clipboard-write berubah: ${status.state}`);
});
}
siapkanFiturSalin();