Kontrol Mode Terang/Gelap dengan JavaScript dan PHP
Membuat kontrol mode terang/gelap pada situs web itu sebenarnya tidak rumit. Kadang yang membuatnya menjadi rumit adalah karena adanya kelas-kelas utilitas.
Tabel Konten
Kelas utilitas adalah kelas-kelas di dalam CSS yang bersifat presentasional (tidak semantik) dan hanya bertujuan untuk mendeskripsikan penampilan di permukaan “apa adanya” sesuai dengan nama kelas tersebut. Beberapa contoh nama kelas yang Saya maksud di sini adalah nama-nama seperti .color-black
, .font-bold
dan .is-hidden
.
Nama-nama kelas seperti ini sangat umum dijumpai di dalam framework seperti Bootstrap, Semantic UI, dan Tailwind CSS. Meskipun berguna untuk mempercepat proses pembuatan aplikasi daring berskala besar, namun fitur-fitur semacam ini biasanya akan menimbulkan masalah ketika pengembang hendak memperbarui desain tanpa harus mengubah struktur HTML, seperti untuk mengubah tampilan menjadi terang atau gelap dengan sekali klik tombol.
Selain untuk menjelaskan mekanisme kontrol tema terang/gelap, Saya juga ingin menunjukkan bagaimana fitur-fitur kelas utilitas dapat membuat mekanisme kontrol terang/gelap menjadi rumit.
Mengubah Mode dengan Klik Tombol
Untuk mengizinkan pengguna mengubah tema secara langsung, biasanya dalam situs web tersebut terdapat sebuah tombol untuk mengubah skema warna dari skema terang ke skema gelap atau sebaliknya. Pada tahap awal, kita buat saja seperti ini:
<a href="#">Tes</a>
Kemudian kita perlu membuat mekanisme agar ketika tautan (tombol) tersebut diklik, maka dia akan menambahkan kelas dark
pada elemen akar, atau sebaliknya, akan menyingkirkan kelas dark
dari elemen akar jika sudah ada:
let root = document.documentElement,
toggle = root.querySelector('a');
toggle.addEventListener('click', e => {
root.classList.toggle('dark');
e.preventDefault();
}, false);
Dengan begitu, setiap kali tombol tersebut diklik maka akan memberikan efek seperti ini di dalam kode HTML:
Dengan menambahkan dan menyingkirkan kelas dark
pada elemen akar sebenarnya sudah cukup untuk mengontrol keseluruhan tampilan karena kode CSS memiliki aturan ruang lingkup. Sehingga kita bisa membuat mode dasarnya sebagai mode terang. Mode gelap dapat dibuat dengan memanfaatkan kehadiran kelas tersebut saja.
Semua kode CSS dapat dinyatakan di dalam satu berkas:
/* Mode terang */
body {
background: #fff;
color: #000;
}
/* Mode gelap */
:root.dark body {
background: #000;
color: #fff;
}
Untuk mengubah label tombol sesuai dengan tema saat itu, kita tinggal mengubah visibilitas label berdasarkan kehadiran kelas dark
seperti ini:
<a href="#">
<span>Gelap</span>
<span>Terang</span>
</a>
:root:not(.dark) a span:nth-child(1),
:root.dark a span:nth-child(2) {
display: none;
}
Menyimpan Mode untuk Dimuat di Sesi Berikutnya
Mekanisme pengubah tema di atas hanya berlaku untuk sesi saat itu saja. Ketika pengguna memuat ulang laman atau berpindah ke laman yang lain di situs web yang sama, maka efek gelap yang sudah diatur akan secara otomatis hilang. Untuk mengatasi masalah tersebut, kita perlu menerapkan fitur penyimpanan lokal pada peramban untuk menyimpan mode tema saat itu sebelum laman ditinggalkan:
toggle.addEventListener('click', e => {
root.classList.toggle('dark');
if (root.classList.contains('dark')) {
localStorage.setItem('is-dark', 1);
} else {
localStorage.removeItem('is-dark');
}
e.preventDefault();
}, false);
Ketika diinspeksi, maka akan muncul item is-dark
dengan nilai "1"
pada mode gelap:
Data tersebut tidak akan terhapus kecuali pengguna secara sengaja menghapusnya. Memuat ulang laman, menutup peramban dan mematikan perangkat tidak akan menghapus data tersebut.
Nilai item akan selalu tersimpan sebagai string, oleh karena itu meskipun Saya menyimpan nilai 1
pada item is-dark
, yang tersimpan nantinya adalah "1"
. Itu sudah menjadi standar:
“The keys and the values are always in the UTF-16
DOMString
format.”
Tapi itu tidak mengapa, karena di sini yang kita butuhkan hanya kuncinya saja yaitu is-dark
. Nilai yang tersimpan di dalam item is-dark
tidak begitu penting selama nilai tersebut bisa dievaluasi sebagai boolean benar.
Dengan mengecek kehadiran kunci tersebut, maka kita bisa menentukan mode gelap secara otomatis ketika laman dimuat kembali:
if (localStorage.getItem('is-dark')) {
document.documentElement.classList.add('dark');
}
Pastikan untuk meletakkan kode tersebut sedekat mungkin dengan elemen akar pembuka. Tempat yang paling ideal menurut Saya adalah setelah pendefinisian set karakter dan sebelum pemuatan berkas CSS:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta content="width=device-width" name="viewport">
<script>
if (localStorage.getItem('is-dark')) {
document.documentElement.classList.add('dark');
}
</script>
<link href="style.css" rel="stylesheet">
<style> … </style>
…
Semakin cepat kelas dark
ditambahkan pada elemen akar, semakin bagus. Karena Saya melihat ada yang menyatakan kondisi penambahan kelas ini terlalu jauh di bawah dokumen sehingga tema gelap akan terlambat datang karena tertahan oleh berkas-berkas CSS dan JavaScript eksternal yang lain yang sedang dimuat sebelum kondisi tersebut berhasil dieksekusi.
Menggunakan Kuki
Fitur kuki pada peramban bisa digunakan sebagai alternatif. Kekurangan dari fitur kuki adalah dia memiliki jangka waktu kadaluarsa (bisa diatasi dengan menyatakan jangka waktu kadaluarsa selama mungkin), dan cenderung rumit untuk diaplikasikan karena kita perlu mengatur waktu kadaluarsa pada setiap item kuki.
toggle.addEventListener('click', e => {
root.classList.toggle('dark');
if (root.classList.contains('dark')) {
document.cookie = 'is-dark=1; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/';
} else {
document.cookie = 'is-dark=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
}
e.preventDefault();
}, false);
if (document.cookie.split(';').some(v => v.includes('is-dark=1'))) {
document.documentElement.classList.add('dark');
}
Kelebihan dari fitur kuki adalah dia biasanya mampu berinteraksi dengan bahasa sisi-peladen, sehingga tema gelap cenderung dapat dipertahankan meskipun fitur JavaScript sudah dimatikan, atau ketika kode JavaScript gagal dieksekusi karena beberapa kesalahan sintaks pada kode-kode JavaScript lain di sekitarnya.
Bahasa Pemrograman Sisi-Peladen untuk Memperkuat Pondasi
Bahasa sisi-peladen yang Saya gunakan sebagai contoh di sini adalah bahasa pemrograman PHP. Bahasa-bahasa pemrograman sisi-peladen yang lain tentu memiliki aturan penulisan yang berbeda untuk mendapatkan data kuki dari peramban. Para pengembang diharapkan dapat menyesuaikan diri.
Karena data penyimpanan lokal peramban tidak bisa diakses oleh PHP, maka kita perlu menggunakan kuki. Kita menggunakan data kuki tersebut untuk mencetak kelas dark
jika item is-dark
ditemukan di dalam $_COOKIE
. Dengan begini, maka kelas dark
dapat ditambahkan ke dalam kode HTML, bahkan sebelum proses pengiriman data dilakukan:
<html class="<?= !empty($_COOKIE['is-dark']) ? 'dark' : ""; ?>">
Catatan: Kode JavaScript di sisi klien untuk mengecek kehadiran item kuki boleh ditiadakan karena sudah diatasi oleh kode PHP di sisi peladen.
Sebagai bonus, tombol pengontrol mode tema juga sebenarnya bisa dibuat supaya bekerja di sisi peladen dengan cara mengecek adanya kueri tertentu. Kode ini harus dinyatakan sebelum tajuk respons dikirim:
if (isset($_GET['is-dark'])) {
$v = (int) $_GET['is-dark'];
if (1 === $v) {
setcookie('is-dark', $v, strtotime('Fri, 31 Dec 9999 23:59:59 GMT'), '/');
} else {
setcookie('is-dark', null, -1, '/');
}
// Variabel `$current_no_query` adalah tautan saat ini tanpa penambahan kueri `is-dark`
// Masing-masing aplikasi memiliki cara tersendiri untuk membentuk “tautan saat ini”
header('location: ' . $current_no_query);
exit;
}
Kemudian, pada tombol kontrol mode tema, pada atribut href
supaya dibuat menjadi seperti ini:
<a href="?is-dark=<?= !empty($_COOKIE['is-dark']) ? 0 : 1; ?>">
Menggunakan Kueri Media
Sebuah fitur baru pada CSS memungkinkan kita untuk mengecek apakah peramban atau perangkat telah diatur ke mode terang atau mode gelap. Tidak semua peramban mendukung fitur ini:
/* Mode terang */
body {
background: #fff;
color: #000;
}
/* Mode gelap */
@media (prefers-color-scheme: dark) {
body {
background: #000;
color: #fff;
}
}
Untuk mengubah mode tidak dilakukan melalui situs web melainkan melalui pengaturan skema warna pada peramban atau perangkat masing-masing.
Melalui JavaScript, kita bisa mengecek apakah kita sedang berada pada mode terang atau gelap dengan cara seperti ini:
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
// Gelap
} else {
// Terang (bawaan?)
}
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
// Terang
} else {
// Gelap (bawaan?)
}
Perubahan juga bisa dikontrol melalui objek media yang didapatkan:
let media = window.matchMedia('(prefers-color-scheme: dark)');
media.addEventListener('change', e => {
console.log(e.matches ? 'Gelap' : 'Terang (bawaan?)');
});
Kontrol Mode dengan Kelas Utilitas
Kontrol mode terang/gelap dengan kelas utilitas hampir tidak bisa dilakukan dengan baik karena kita perlu menyeleksi semua elemen HTML yang diperlukan untuk ditambahi kelas utilitas mode gelap. Kalau kalian menggunakan AJAX, biasanya kode HTML yang dibentuk dari hasil respons perlu untuk diseleksi kembali sehingga akan menambah beban kerja JavaScript.
Berikut ini adalah contoh penerapan pada Bootstrap, untuk mengubah warna bar navigasi saja, tanpa menyimpan data ke penyimpanan lokal:
let navbars = document.querySelectorAll('.navbar'),
btn = document.querySelector('.btn');
btn.addEventListener('click', e => {
navbars.forEach(navbar => {
navbar.classList.toggle('bg-dark');
navbar.classList.toggle('navbar-dark');
});
e.preventDefault();
}, false);
Tidak praktis.
9 Komentar
Sugeng
Saat ini saya menggunakan metode klik tombol untuk fitur mode gelap.
Dari membaca artikel ini saya mempunyai ide untuk mengkombinasikan metode klik tombol dengan kueri media.
Kurang lebih cara kerjanya seperti ini:
Jika berkenan mungkin mas Taufik bisa memberikan contoh penerapannya supaya saya tidak terlalu pusing menyusun kodenya dari nol. hehe..
Terima kasih.
Taufik Nurrohman
Tergantung yang mau diprioritaskan yang mana mas. Mau memprioritaskan fitur natif atau fitur klik tombol.
Kalau ingin memprioritaskan fitur natif, kita cukup nambahin duplikat kode CSS mode gelap, tapi tanpa kelas mode gelap:
Kemudian di bagian JavaScript, supaya dilepas saja kelas mode gelapnya kalau memang sudah mendukung fitur mode gelap melalui perangkat:
Kekurangannya, kode CSS untuk mode gelap harus ditulis dua kali. Satu untuk versi kelas dan satu untuk versi kueri media.
Kalau ingin memprioritaskan fitur klik tombol, kode CSS bisa dibiarkan seperti apa adanya. Cuma di bagian JavaScript kita supaya nggak cuma mengecek fitur penyimpanan lokal tapi juga perlu mengecek fitur mode gelap natif:
Kekurangannya, kalau berdasarkan kode di atas sih seharusnya fitur natif bakalan mengalahkan status mode gelap dari fitur klik tombol. Kalau saja ada cara untuk mengubah pengaturan mode gelap di perangkat melalui JavaScript, kita bisa memaksa perangkat untuk mematikan pengaturan mode gelapnya. Tapi sepertinya itu tidak mungkin bisa dilakukan karena masalah privasi pengguna (bisa digunakan untuk memicu praktik XSS). Jadi efeknya, kalau pengguna mencoba mengganti ke mode gelap sedangkan pada perangkat dia memakai mode terang, efeknya bakalan hilang di sesi berikutnya karena perintah di blok
else
.Taufik Nurrohman
Atau pakai cara ini mungkin bisa mas, supaya fitur natif nggak begitu memaksa. Dengan mengecek aksi perubahan mode dari preferensi untuk mengatur nilai penyimpanan lokal:
Taufik Nurhidayat
Kalau dalam dark mode lebih bagus menggunakan variabel CSS, jadi lebih simple.
Mirat
Nemu kode, ini CSS trick, siapa tau bermanfaat.
Pemuja
Mas saya mau tanya , pembuatan js add class tapi class nya muncul saat klik terakhir pada button nya .
Taufik Nurrohman
Pakai event
mouseup
dantouchend
saja, jangan pakai eventclick
.Hafiz
Mas, saya mau tanya. Kalau biar code di bawah bisa jadi toggle add/remove class memanfaatkan classlist gimana ya?
$(document).on("click", ".btn-langue", function () { const bahasa = document.querySelector(".bahasa"); const en = document.querySelector(".en");
});
suntuy
Mungkin ngak simpan beberapa mode, misal ada color picker untuk ubah warna text, aku coba yang dark mode, kesimpan tapi edit kodenya untuk picker warna, tapi bentrok