Monday, November 4, 2013

Throttle dan Debounce

Throttle and Debounce Diagram

Dalam JavaScript, istilah throttle dan debounce biasa digunakan untuk menyatakan mengurangi atau menunda eksekusi fungsi yang bekerja berkali-kali, misalnya ketika pengguna sedang mengubah ukuran layar atau menggerak-gerakkan pointer mouse. Tujuan utama dari metode ini adalah untuk memastikan agar eksekusi fungsi bisa bekerja seefektif dan seefisien mungkin sekaligus juga sejarang mungkin, sehingga peramban tidak akan terlalu terbebani di dalam mengeksekusi fungsi yang terjadi berkali-kali, yang belum tentu juga fungsi tersebut memang perlu untuk dijalankan sesering itu.

Throttle

Metode ini berdasar pada sebuah usaha untuk membuat jumlah eksekusi fungsi menjadi lebih jarang dari event yang terjadi. Di sini, fungsi bernama foo() akan dieksekusi setiap 1 detik sekali selama kita menggerak-gerakkan pointer mouse di atas dokumen dan bukannya setiap kali pointer mouse berkerak:

var delay = 1000, // Interval pembatas/penundaan eksekusi berikutnya (milidetik)
    previousCall = new Date().getTime(); // Set waktu kadaluarsa awal
document.onmousemove = function() {
    var time = new Date().getTime();
    // Bandingkan antara waktu terakhir kali eksekusi dengan waktu setiap kali event bekerja.
    // Jika selisihnya sudah mencapai/melebihi `delay`, jalankan fungsi `foo()`
    if ((time - previousCall) >= delay) {
        foo();
        previousCall = time; // Set ulang waktu kadaluarsa
    }
};

function foo() {
    console.log('test!');
}

Lihat Demo

Debounce

Metode ini berdasar pada sebuah usaha untuk membuat fungsi tereksekusi hanya jika pengguna telah berhenti melakukan suatu event yang berulang, atau hanya dieksekusi sekali saja saat pertama kali event terjadi. Misalnya ketika pengguna berhenti menggerakkan pointer mouse saja atau ketika pengguna mulai menggerakkan pointer mouse saja. Yang paling umum diterapkan adalah kasus yang pertama.

Fungsi di bawah ini terdiri dari penunda berupa setTimeout di dalam jenis event sensitif yang bisa mengeksekusi fungsi berkali-kali setiap kali pengguna menggerakkan pointer mouse di atas dokumen. Namun karena pada saat yang bersamaan fungsi clearTimeout menggagalkan setTimeout untuk mencapai akhir waktu tunda, maka fungsi foo() tidak akan pernah bisa tereksekusi sebelum pengguna benar-benar menghentikan gerakkan pointer mouse mereka:

var timer = null;
document.onmousemove = function() {
    if (timer) clearTimeout(timer); // Hentikan `setTimeout` sesegera mungkin agar `foo()` tidak tereksekusi
    timer = setTimeout(function() {
        foo(); // Jalankan fungsi `foo()` jika timer tidak lagi dihentikan oleh `clearTimeout`
        timer = null;
    }, 300);
};

function foo() {
    console.log('test!');
}

Waktu penunda selama 300 milidetik dibuat hanya sebagai toleransi saja untuk memastikan agar waktu eksekusi berjalan lebih lambat dari pergerakkan pointer mouse pengguna:

Lihat Demo

Beberapa Event JavaScript yang Masuk dalam Kategori Sensitif

Saya menyebutnya sebagai event yang sensitif karena event ini bisa mengeksekusi fungsi berkali-kali dengan cepat jika sedang dikerjakan oleh pengguna. Beberapa di antaranya adalah:

  • onmousemove (menggerakkan)
  • onscroll (menggulung)
  • onresize (mengubah ukuran)
  • onkeyup (mengetik)
  • onkeydown (mengetik)
  • onkeypress (mengetik)

Contoh Penerapan dalam Kasus Nyata

Berikut ini adalah sebuah gambaran penerapan debounce untuk mencegah AJAX memanggil data berkali-kali pada saat pengguna mengetik kata kunci pencarian. Di sini, data hanya akan terpanggil hanya jika pengguna telah berhenti mengetik:

<input type="text" id="search-field">
<div id="search-result"></div>

<script src="js/jquery.js"></script>
<script>
var timer = null;
$('#search-field').on("keydown", function() {
    var keyword = this.value;
    if (timer) clearTimeout(timer);
    timer = setTimeout(function() {
        // Panggil data!
        $.get('http://sumber_data.com/search?q=' + keyword, function(data) {
            $('#search-result').html($(data));
        });
        timer = null;
    }, 300);
});
</script>

Saya tidak begitu sering melihat penerapan metode throttle dalam kasus yang nyata. Tapi pada intinya, metode ini berfungsi untuk menunda fungsi agar fungsi tersebut tereksekusi dalam interval waktu tertentu saja pada saat pengguna menjalankan event.

Beberapa manfaat lain dari metode ini:

  1. Membuat aplikasi yang dapat menyimpan data secara otomatis setiap kali pengguna berhenti mengetik.
  2. Fitur penelusuran dengan AJAX yang memungkinkan pengguna untuk memanggil data tanpa harus memaksa pengguna untuk menekan tombol Submit. Cukup dengan mengetik kata kunci dan berhenti, maka hasil penelusuran akan segera dimuat.
  3. Mengubah dan mengatur ulang posisi elemen dalam jumlah yang banyak dengan perhitungan yang rumit setiap kali pengguna mengubah ukuran layar pada interval tertentu.

Labels: ,

Saturday, November 2, 2013

Border Semitransparan untuk Menciptakan Efek Tajam dan Tipis pada Elemen

Saya secara alami sering dan suka memperhatikan hal-hal yang detail, terutama pada elemen-elemen UI di dalam web-web terkenal seperti Google, Facebook dan juga berbagai framework web. Kali ini Saya akan membahas mengenai tampilan menu dropdown pada framework Bootstrap ini:

Bootstrap Dropdown
Menu dropdown pada Bootstrap.

Tampilannya terlihat sangat tajam dan dimensinya juga terlihat sangat tipis! Bagi Anda yang sudah terbiasa bekerja dengan aplikasi Photoshop dan sejenisnya mungkin tidak begitu asing dengan teknik untuk membuat efek seperti ini.

Pada kenyataannya, menciptakan elemen kotak dengan efek bayangan saja tidak cukup untuk memberikan kesan sisi yang tajam dan dimensi yang tipis seperti gambar di atas:

/* Belum cukup! */
.box {
  background-color:white;
  border:1px solid #bbb;
  box-shadow:0 4px 15px -4px rgba(0,0,0,.6);
}

Lihat Demo

Deklarasi di atas sudah cukup untuk menciptakan efek kotak bergaris batas dan berbayang, namun tidak cukup utuk memberikan efek tajam pada elemen tersebut. Jika Anda memperhatikan dengan seksama pada menu dropdown di gambar pertama, maka seharusnya Anda akan menyadari bahwa garis batas pada menu tersebut ternyata tembus pandang:

Bootstrap Dropdown
Pembesaran tampilan.

Efek semacam ini tidak cukup dibuat hanya dengan warna solid dan bayangan, melainkan kita membutuhkan warna semi transparan pada border. Kita bisa menggunakan kode warna RGBA atau HSLA pada border:

.box {
  background-color:white;
  border:1px solid rgba(0,0,0,.3);
  box-shadow:0 4px 15px -4px rgba(0,0,0,.6);
}

Tapi sekarang efek transparan pada border justru menampilkan warna latar dari elemen .box di bagian bawahnya karena warna latar secara normal memang akan menembus sampai ke sisi terluar dari border (Anda bisa mengeceknya dengan mencoba mengubah tipe border menjadi dashed atau dotted). Untuk mencegah warna latar agar tidak melebihi batas dalam lingkar border, kita bisa menggunakan CSS background-clip dengan nilai padding-box:

.box {
  background-color:white;
  border:1px solid rgba(0,0,0,.3);
  background-clip:padding-box;
  box-shadow:0 4px 15px -4px rgba(0,0,0,.6);
}

Lihat Demo

Membandingkan Hasil Tampilan

Berikut ini adalah gambar cuplikan hasil tampilan pada efek sebelum dan efek sesudah:

Efek Tajam dengan CSS
Efek sebelum dan sesudah.

Penggunaan Properti background-clip Lainnya

Fungsi CSS background-clip adalah untuk menentukan apakah latar belakang elemen, baik warna atau gambar, akan meluas ke bagian bawah perbatasan atau tidak. Nilai padding-box pada properti ini memungkinkan warna latar yang secara normal akan melebar dan melebihi batas sebelah dalam dari lingkar border menjadi tidak melebihi batas tersebut. Selengkapnya mengenai CSS background-clip bisa Anda pelajari di sini

Secara pribadi Saya jarang menggunakannya untuk kepentingan dekorasi. Melainkan, Saya lebih sering menggunakan properti ini untuk memperbaiki tampilan yang sedikit rusak karena render piksel yang tidak terlalu baik pada beberapa peramban. Sebagai contoh adalah pada elemen tombol dengan efek rouded corner di sekelilingnya seperti ini:

CSS3 Button

Gambar sebelah kiri adalah tampilan standar tombol yang dibuat tanpa mendeklarasikan background-clip:padding-box. Jika Anda meperhatikannya dengan seksama, maka pada bagian sisi-sisi lengkungnya akan tampak sedikit warna latar yang melebihi batas. Saya tidak tahu mengapa, tetapi ini hanya terjadi pada elemen tombol yang dikenai deklarasi display:inline, sedangkan tombol dengan deklarasi display:inline-block atau display:block tidak mengalami masalah ini. Ini terjadi di Google Chrome.

Gambar sebelah kanan adalah hasil tampilan sesudah Saya menerapkan deklarasi background-clip:padding-box pada tombol. Terlihat lebih rapi, karena warna latar tidak menembus bagian sisi tombol.

Kembali kepada efek tajam dan super tipis pada menu dropdown, Google Chrome juga mengimplementasikan hal yang sama, hanya saja sepertinya dia menggunakan latar gambar, bukan CSS:

Google Chrome Dropdown
Menu dropdown pada Google Chrome.

Pada Google Chrome versi lama, Anda pasti pernah melihat sebuah menu di bagian kanan bawah bernama “Barusan Ditutup” yang jika diklik akan menampilkan daftar histori munculan yang juga memiliki kesan yang sama seperti yang sedang kita bahas sekarang, akan tetapi efek itu diciptakan menggunakan metode yang sedikit berbeda dan Saya pikir ini sudah tidak standar lagi karena hasilnya yang kurang rapi dan tidak konsisten. Bukan menggunakan border semitransparan dan CSS background-clip, melainkan menggunakan outline semitransparan. Hasilnya kurang bagus karena outline tidak bisa mengikuti efek lengkung di ujung-ujung kotak. Saya menambahkan ini sebagai pengetahuan tambahan saja. Seharusnya Saya membahas ini sebelum peramban tersebut diperbaharui sampai yang seperti sekarang:

Outline Semitransparan
Outline tidak bisa mengikuti garis lengkung.
.box {
  background-color:white;
  box-shadow:0 0 3px rgba(0,0,0,.8);
  outline:1px solid rgba(0,0,0,.4);
  outline-offset:-3px;
}

Teknik lainnya adalah dengan menggunakan CSS bayangan murni, tanpa border:

.box {
  background-color:white;
  box-shadow:0 0 0 1px rgba(0,0,0,.3),0 4px 15px -4px rgba(0,0,0,.6);
}

Lihat Demo

Ini adalah teknik tersingkat, karena kita bahkan tidak memerlukan properti background-clip untuk menciptakan efeknya di sini. Hanya saja, jika peramban tidak mendukung CSS bayangan atau tidak mendukung CSS bayangan berganda atau tidak mendukung nilai spread pada CSS bayangan, maka efek garis batasnya tidak akan terlihat. Border memungkinkan kita untuk menciptakan fallback agar tampilan elemen tidak menjadi terlalu buruk pada peramban yang tidak mendukung CSS bayangan dan warna RGBA. Begini maksudnya:

.box {
  background-color:white;
  border:1px solid #bbb; /* warna fallback untuk peramban yang tidak mendukung warna RGBA */
  border:1px solid rgba(0,0,0,.3); /* warna border yang seharusnya */
  background-clip:padding-box;
  box-shadow:0 4px 15px -4px rgba(0,0,0,.6);
}

Labels: