Selection dan data join adalah cara D3 menghubungkan array data dengan elemen dokumen. Topik ini menjelaskan selection sebagai penunjuk elemen, data join beserta tiga grupnya yang bernama enter, update, dan exit, peniruan pola join pada array biasa di Node, dan demo join sungguhan yang menggambar batang pada satu SVG. Setelah membacanya, perubahan tampilan akibat pergantian data dapat dijelaskan menurut grup yang bekerja.
Selection sebagai penunjuk elemen#
Selection adalah objek yang menunjuk sekumpulan elemen dokumen yang telah dipilih. Fungsi d3.select memilih elemen pertama yang cocok dengan penyeleksi, sedangkan d3.selectAll memilih semua elemen yang cocok. Keduanya mengikuti aturan penyeleksi CSS yang sama dengan querySelector dan querySelectorAll pada JavaScript biasa, dengan penyeleksi berupa teks seperti "#gambar" untuk satu elemen dengan id gambar atau "rect" untuk seluruh persegi panjang.
Yang membedakan selection dari hasil querySelector adalah perilakunya. Selection menyediakan metode untuk mengubah elemen yang ditunjuk, yaitu attr untuk atribut, style untuk gaya, text untuk isi teks, append untuk menambah elemen anak, dan on untuk memasang pendengar peristiwa. Setiap metode mengembalikan selection yang sama sehingga pemanggilan dapat dirangkai dalam satu pernyataan. Perubahan diterapkan langsung ke dokumen, sehingga selection berperan sebagai penunjuk, bukan salinan elemen. Pemilihan juga dapat diturunkan melalui selectAll yang dijalankan pada sebuah selection untuk menata label di dalam baris, dan karena selection hanyalah penunjuk, pemilihan ulang dilakukan setiap kali elemen baru muncul.
Data join dan tiga grupnya#
Data join mengikat array ke sekumpulan elemen melalui pemanggilan data() pada selection. Setelah array terikat, elemen dan data terbagi ke tiga grup. Grup update berisi pasangan yang cocok, yaitu elemen yang telah memiliki datanya. Grup enter berisi data yang belum memiliki elemen, sedangkan grup exit berisi elemen yang datanya hilang. Tanpa argumen kunci, pemetaan dilakukan menurut urutan indeks, sehingga data ke-i berpasangan dengan elemen ke-i.
Diagram berikut merangkum tiga grup tersebut beserta akibatnya pada dokumen.
Metode join() menangani ketiga grup sekaligus dalam satu pemanggilan: elemen baru dibuat untuk grup enter, elemen yang sudah ada diperbarui untuk grup update, dan elemen yang tidak lagi memiliki pasangan data dihapus dari grup exit. Bentuk lainnya menerima tiga fungsi yang memungkinkan setiap grup diperlakukan secara terpisah. Bentuk ini digunakan ketika masing-masing grup memerlukan perlakuan yang berbeda. Keunggulan pola ini terletak pada pendekatannya yang deklaratif: kode cukup menyatakan hubungan akhir antara data dan elemen, sedangkan D3 menangani proses penambahan, pembaruan, dan penghapusan secara otomatis. Dengan demikian, tidak diperlukan perulangan manual untuk menghitung selisih panjang array.
Pola join yang ditiru pada array biasa#
Berkas berikut meniru pola join tanpa DOM agar mekanismenya terlihat. Fungsi join membagi data dan elemen lama menjadi tiga grup menurut panjang keduanya, lalu fungsi render mencetak isinya untuk tiga keadaan. Data yang digunakan adalah nilai matematika dua belas siswa kelas 7A dan 7B, diambil dari berkas nilai_ujian.csv yang digunakan sepanjang jalur dan ditulis tetap di dalam berkas agar potongannya berdiri sendiri. Jalankan berkas itu dari folder topik dengan perintah node join.js.
// join.js: pola data join D3 yang ditiru pada array biasa, tanpa DOM.
// Jalankan dari folder topik: node join.js
// Data: nilai matematika pada berkas nilai_ujian yang dipakai di sepanjang
// jalur (kelas 7A dan 7B, dua belas siswa per kelas).
const nilai7A = [
["Ali", 78], ["Budi", 85], ["Citra", 92], ["Dewi", 68], ["Eka", 74],
["Fajar", 88], ["Gita", 81], ["Hana", 90], ["Indra", 66], ["Joko", 77],
["Kartika", 95], ["Laila", 83],
];
const nilai7B = [
["Nanda", 72], ["Oki", 80], ["Putri", 87], ["Rahmat", 63], ["Sari", 76],
["Tono", 84], ["Umar", 70], ["Vina", 91], ["Wulan", 79], ["Yusuf", 65],
["Zahra", 89], ["Bayu", 75],
];
// join membagi data dan elemen lama menjadi tiga grup menurut urutan indeks
function join(data, elemenLama) {
const jumlahPasangan = Math.min(data.length, elemenLama.length);
return {
update: data.slice(0, jumlahPasangan),
enter: data.slice(jumlahPasangan),
exit: elemenLama.slice(jumlahPasangan),
};
}
function render(judul, data, elemenLama) {
const hasil = join(data, elemenLama);
console.log(judul);
console.log(` enter : ${hasil.enter.length} data baru`);
console.log(` update : ${hasil.update.length} pasangan`);
console.log(` exit : ${hasil.exit.length} elemen`);
return data; // seluruh data kini terikat pada elemennya
}
// render pertama: belum ada elemen, seluruh data kelas 7A masuk enter
let elemen = render("render 1, data kelas 7A (12 nilai), elemen lama 0", nilai7A, []);
// pergantian ke kelas 7B: jumlahnya sama, semua pasangan masuk update
elemen = render("render 2, data kelas 7B (12 nilai), elemen lama 12", nilai7B, elemen);
// penyaringan lima siswa pertama 7B: elemen lebih banyak daripada data
elemen = render("render 3, lima siswa pertama 7B (5 nilai), elemen lama 12", nilai7B.slice(0, 5), elemen);render 1, data kelas 7A (12 nilai), elemen lama 0
enter : 12 data baru
update : 0 pasangan
exit : 0 elemen
render 2, data kelas 7B (12 nilai), elemen lama 12
enter : 0 data baru
update : 12 pasangan
exit : 0 elemen
render 3, lima siswa pertama 7B (5 nilai), elemen lama 12
enter : 0 data baru
update : 5 pasangan
exit : 7 elemenKetiga render memperlihatkan tiga kondisi yang berbeda. Render pertama dijalankan ketika elemen masih kosong, sehingga dua belas nilai semuanya masuk enter. Render kedua mengganti kelas dengan jumlah yang sama, sehingga dua belas pasangan masuk update dan tidak ada elemen yang muncul atau hilang. Render ketiga menyaring lima siswa pertama, sehingga lima pasangan diperbarui dan tujuh elemen sisa masuk exit. Inilah pembukuan yang dikerjakan D3 setiap kali data() dan join() dipanggil pada dokumen sungguhan.
Demo: satu SVG, tiga grup berganti#
Demo berikut menjalankan join sungguhan pada dokumen, dengan pustaka dimuat dari berkas d3.min.js yang berada di bundle topik. Tiga tombolnya menampilkan dua belas batang kelas 7A, dua belas batang kelas 7B, dan lima batang pertama dari kelas 7B, semuanya digambar pada satu SVG yang sama. Tekan tombol-tombolnya secara bergantian dan amati batang yang muncul, berubah, dan hilang. Tinggi setiap batang mengikuti nilai apa adanya dalam piksel, dan teks status di bawah tombol menyebut grup yang bekerja pada setiap pergantian.
Ketika kelas 7A diganti kelas 7B, tidak ada batang yang muncul atau hilang karena jumlahnya sama, yaitu seluruhnya masuk grup update dan hanya nilai pada atributnya yang berganti. Ketika data disaring menjadi lima siswa, tujuh batang sisa dihapus melalui grup exit, dan peralihan kembali ke data penuh memunculkannya lagi melalui grup enter. Satu pemanggilan join melayani ketiga kemungkinan tersebut tanpa percabangan yang menghitung selisih jumlah batang.
Pola join pada dokumen#
// tidak-dijalankan
const batang = d3.select("#gambar") // penunjuk satu elemen SVG
.selectAll("rect") // penunjuk seluruh rect di dalamnya
.data(nilai) // array diikat menurut urutan indeks
.join("rect"); // enter, update, dan exit diatur sekaligus
batang
.attr("x", (d, i) => 30 + i * 34)
.attr("width", 26)
.attr("y", (d) => 200 - d)
.attr("height", (d) => d);Rangkaian pada potongan kode pertama melakukan empat langkah secara berurutan: memilih elemen SVG, memilih seluruh elemen rect di dalamnya, mengikat array nilai ke elemen tersebut, lalu mengatur ketiga grup menggunakan join(). Fungsi akses seperti (d, i) dipanggil untuk setiap pasangan data dan elemen, dengan d mewakili nilai data dan i menunjukkan indeksnya. Selection yang dikembalikan oleh join() sudah mencakup elemen baru dan elemen yang diperbarui. Oleh karena itu, atribut yang ditetapkan setelah pemanggilan tersebut berlaku untuk keduanya, sedangkan elemen yang tidak lagi memiliki pasangan data langsung dihapus.
// tidak-dijalankan
svg.selectAll("rect")
.data(nilai)
.join(
(enter) => enter.append("rect").attr("fill", "#4477aa"),
(update) => update.attr("stroke", "#22304a"),
(exit) => exit.remove()
);Bentuk yang menerima tiga fungsi digunakan ketika setiap grup memerlukan perlakuan berbeda. Sebagai contoh, batang baru diberi warna penuh, batang yang sudah ada hanya diberi garis tepi, sedangkan elemen yang tidak lagi memiliki pasangan data dihapus. Dengan demikian, perilaku bawaan join("rect") setara dengan penggunaan ketiga fungsi tersebut tanpa perlakuan khusus: elemen baru dibuat, elemen yang sudah ada dipertahankan, dan elemen yang tidak lagi memiliki pasangan data dihapus. Kedua bentuk tersebut menjalankan mekanisme pengelolaan yang sama seperti simulasi menggunakan array biasa sebelumnya, tetapi kali ini diterapkan pada elemen dokumen SVG yang sebenarnya.
Kesalahan umum#
- Menghapus elemen lama secara manual sebelum join. Elemen yang dihapus di luar join membuat grup exit tidak pernah berisi apa pun, dan pembanding visual antarkeadaan hilang. Serahkan seluruh siklus tambah, ubah, dan hapus kepada
join(). - Mengikat data yang urutannya berubah tanpa fungsi kunci. Pemetaan menurut indeks membuat pasangan bertukar elemen sebagaimana dijelaskan pada peringatan di atas. Perbaikannya berupa fungsi kunci, misalnya
data(nilai, (d) => d.nama), sehingga nilai menelusuri elemennya sendiri. - Menganggap selection sebagai salinan elemen. Metode pada selection langsung mengubah dokumen, sehingga selection yang disimpan tidak menyimpan keadaan lampau. Apabila keadaan sebelumnya diperlukan, data yang terikat dapat dibaca melalui
datum()atau disimpan sendiri pada saat render. - Menetapkan atribut sebelum data diikat. Fungsi akses pada
attrmembaca nilai dataddari pasangannya, sehingga rantai yang menetapkan atribut sebelumdata()tidak memiliki nilai untuk dibaca. Urutan yang benar berupadata()terlebih dahulu, lalujoin(), dan pengubahan atribut menjadi langkah terakhir.
Ringkasan#
- Selection adalah penunjuk sekumpulan elemen dokumen yang dapat langsung diubah melalui
attr,style,text,append, danon. - Data join mengikat array ke elemen dan membaginya menjadi tiga grup, yaitu enter untuk data tanpa elemen, update untuk pasangan, dan exit untuk elemen tanpa data.
- Satu pemanggilan
join()mengatur ketiga grup sekaligus, sehingga penghitungan penambahan dan penghapusan elemen tidak ditulis manual. - Pemetaan bawaan menurut indeks, dan fungsi kunci pada
data()menjaga setiap nilai tetap pada elemennya.
Referensi#
- d3-selection — D3 — Dokumentasi resmi selection beserta metode pemilihan dan pengubahannya.
- Joining data — D3
— Dokumentasi resmi
data,enter,exit, danjoinbeserta fungsinya.