Toko buku Budi baru saja mendapat form pendaftaran member: kolom nama, dropdown kota, kotak centang genre favorit, dan pilihan format buku. Semua sudah tergambar rapi dengan HTML. Masalahnya satu: begitu pengunjung menekan tombol daftar, aplikasi belum punya cara membaca isi yang mereka ketik. Data pengguna hidup di dalam elemen form, dan hanya JavaScript yang bisa mengambilnya.

Chapter ini membahas dua jalur pembacaan data form. Jalur pertama menyasar elemen satu per satu lewat properti seperti .value dan .checked; jalur kedua merangkum seluruh form sekaligus dalam satu object FormData, lalu mengubahnya menjadi object JavaScript biasa dengan Object.fromEntries(). Setelah chapter ini, kita bisa membaca form apa pun tanpa menebak di mana nilai tiap kontrol disimpan.

Membaca Nilai dengan .value #

Aturannya tunggal: setiap kontrol teks — <input>, <select>, dan <textarea> — menyimpan isi terkininya di properti .value, dan nilai itu selalu berupa string. Angka yang diketik pun datang sebagai "2", bukan 2. Ketika pengguna mengetik atau memilih, browser memperbarui .value secara langsung, sehingga membaca properti ini kapan pun selalu menghasilkan isi terbaru — bukan potongan lama yang perlu disinkronkan manual.

Satu perbedaan kecil perlu diingat: .value adalah properti DOM yang hidup, bukan isi atribut HTML. Atribut value="..." pada HTML hanya menyediakan nilai awal; perubahan lewat keyboard tidak menyentuh atribut tersebut, tetapi selalu terlihat di .value.

Kapan membacanya juga bebas — dan pilihan itu mengikuti kebutuhan. Membaca .value di dalam handler input atau change (lihat kembali chapter Form Events) memberi nilai pada momen perubahannya; membaca sekali di handler submit memberi seluruh isian terakhir sekaligus. Karena properti ini selalu memuat keadaan terkini, tidak ada mekanisme penyinkronan yang perlu kita tulis: cukup baca saat dibutuhkan.

Berkas berikut membaca satu form pendaftaran member yang memuat kelima jenis kontrol yang paling sering dipakai:

html
<form id="form-member">
  <label for="nama">Nama Lengkap</label>
  <input id="nama" name="nama" type="text">

  <label for="kota">Kota</label>
  <select id="kota" name="kota">
    <option value="jakarta">Jakarta</option>
    <option value="bandung">Bandung</option>
    <option value="yogyakarta">Yogyakarta</option>
  </select>

  <label for="catatan">Catatan</label>
  <textarea id="catatan" name="catatan"></textarea>

  <input type="checkbox" id="minat-fiksi" name="minat" value="fiksi">
  <label for="minat-fiksi">Fiksi</label>
  <input type="checkbox" id="minat-sejarah" name="minat" value="sejarah">
  <label for="minat-sejarah">Sejarah</label>

  <input type="radio" id="fmt-cetak" name="format" value="cetak">
  <label for="fmt-cetak">Cetak</label>
  <input type="radio" id="fmt-digital" name="format" value="digital">
  <label for="fmt-digital">Digital</label>
</form>
// baca-data-member.js — membaca nilai tiap kontrol langsung dari elemennya
const form = document.querySelector("#form-member");

const nama = form.querySelector("#nama");                      // input teks
const kota = form.querySelector("#kota");                      // select
const catatan = form.querySelector("#catatan");                // textarea
const minat = form.querySelectorAll('input[name="minat"]');    // checkbox
const format = form.querySelectorAll('input[name="format"]');  // radio

// --- simulasi pengisian oleh pengguna ---
nama.value = "Siti Rahma";
kota.value = "bandung";
catatan.value = "Suka buku sejarah Bandung.";
minat[0].checked = true;
format[1].checked = true;

// .value selalu string, apa pun jenis kontrolnya
console.log("nama    :", nama.value);
console.log("kota    :", kota.value);
console.log("catatan :", catatan.value);

// checkbox dibaca lewat .checked (boolean)
console.log("minat   :", minat[0].checked, minat[1].checked);

// radio: cukup satu yang checked dalam satu grup
for (const pilihan of format) {
  if (pilihan.checked) {
    console.log("format  :", pilihan.value);
  }
}

Output yang Dihasilkan (console browser):

text
nama    : Siti Rahma
kota    : bandung
catatan : Suka buku sejarah Bandung.
minat   : true false
format  : digital

Mari kita bedah baris-baris pentingnya:

  1. Baris 2 (const form = document.querySelector("#form-member")): form sebagai pembungkus dicari lebih dulu, lalu setiap kontrol dicari relatif terhadapnya dengan form.querySelector. Pencarian relatif menjaga kode tetap benar bila halaman berisi lebih dari satu form.
  2. Baris 4–8 (daftar kontrol): input, select, dan textarea cukup dirujuk satu per satu; checkbox dan radio yang berbagi name yang sama dikumpulkan lewat querySelectorAll karena jumlahnya bisa lebih dari satu.
  3. Baris 11–15 (simulasi pengisian): penugasan .value pada select langsung memilih opsi yang value-nya cocok — kota.value = "bandung" sama saja dengan memilih “Bandung” di dropdown.
  4. Baris 18–20 (membaca .value): ketiga kontrol teks dibaca dengan properti yang sama, dan hasilnya selalu string: "Siti Rahma", "bandung", serta isi textarea apa adanya.
  5. Baris 23 (membaca .checked): checkbox melaporkan statusnya sebagai boolean. Properti .value pada checkbox tetap ada, tetapi isinya hanya relevan ketika kotaknya dicentang.
  6. Baris 26–30 (grup radio): dalam satu grup radio dengan name sama, hanya satu tombol yang bisa checked. Kita menelusuri seluruh tombol dan mengambil value dari satu-satunya yang terpilih.

Checkbox: Status di .checked, Bukan .value #

Kalimat “kotaknya dicentang atau tidak” diterjemahkan DOM menjadi boolean di properti .checked. Membaca .value pada checkbox justru sering menipu: nilainya tetap "fiksi" meskipun kotaknya kosong. Membaca .checked dulu, baru mengambil .value bila kotak tercentang, adalah urutan yang benar.

Berkas baca-data-member.js memperlihatkan polanya pada baris 23: minat[0].checked menghasilkan true, minat[1].checked menghasilkan false, dan keduanya tidak saling memengaruhi karena checkbox bersifat independen — berbeda dengan radio.

Grup Radio: Satu Pilihan dari Banyak Tombol #

Radio adalah kebalikan dari checkbox: seluruh tombol yang berbagi name yang sama membentuk satu grup, dan browser memastikan hanya satu yang aktif. Karena itu, membaca radio selalu berarti mencari “satu yang checked di antara banyak” — persis pola loop pada baris 26–30. Setelah format[1].checked = true dijalankan, tombol cetak otomatis nonaktif; keduanya tidak mungkin aktif bersamaan.

FormData: Seluruh Form dalam Satu Object #

Menyusuri kontrol satu per satu bekerja, tetapi form yang panjang membuat kode pembacaan ikut memanjang. Interface FormData mengambil alih tugas itu: new FormData(form) menelusuri seluruh kontrol yang punya atribut name, membaca statusnya sesuai jenis kontrol — .value untuk teks, status .checked untuk checkbox dan radio — lalu menyusun semuanya sebagai kumpulan pasangan kunci-nilai.

// kumpulkan-formdata.js — FormData merangkum seluruh kontrol ber-name
const form = document.querySelector("#form-member");

// isi ulang form seperti pengguna
form.querySelector("#nama").value = "Siti Rahma";
form.querySelector("#kota").value = "bandung";
form.querySelector("#catatan").value = "Suka buku sejarah Bandung.";
form.querySelector('input[name="minat"][value="fiksi"]').checked = true;
form.querySelector('input[name="minat"][value="sejarah"]').checked = true;
form.querySelector('input[name="format"][value="digital"]').checked = true;

const data = new FormData(form);
console.log("nama   :", data.get("nama"));
console.log("minat  :", data.getAll("minat").join(", "));
console.log("format :", data.get("format"));

// Object.fromEntries mengubah FormData menjadi object biasa
const ringkas = Object.fromEntries(data);
console.log("ringkas:", JSON.stringify(ringkas));

// Object.fromEntries hanya menyimpan satu nilai per kunci — nilai terakhir menang.
// Bila multi-nilai perlu dipertahankan, akumulasi secara manual:
const lengkap = {};
for (const [kunci, nilai] of data) {
  if (kunci in lengkap) {
    lengkap[kunci] = [].concat(lengkap[kunci], nilai);
  } else {
    lengkap[kunci] = nilai;
  }
}
console.log("lengkap:", JSON.stringify(lengkap));

Output yang Dihasilkan (console browser):

text
nama   : Siti Rahma
minat  : fiksi, sejarah
format : digital
ringkas: {"nama":"Siti Rahma","kota":"bandung","catatan":"Suka buku sejarah Bandung.","minat":"sejarah","format":"digital"}
lengkap: {"nama":"Siti Rahma","kota":"bandung","catatan":"Suka buku sejarah Bandung.","minat":["fiksi","sejarah"],"format":"digital"}

Yang perlu kita perhatikan:

  1. Baris 12 (new FormData(form)): satu baris menggantikan seluruh loop pembacaan manual. Method data.get("nama") mengembalikan satu nilai, sedangkan data.getAll("minat") mengembalikan array karena kunci minat diisi dua checkbox yang sama-sama tercentang.
  2. Baris 18–19 (Object.fromEntries(data)): FormData mudah diubah menjadi object biasa, dan object hasilnya nyaman disimpan, dicatat, atau dikirim. Tapi lihat baris ringkas pada keluaran: kunci minat hanya berisi "sejarah" — padahal dua kotak dicentang.
  3. Baris 21–30 (pola akumulasi): Object.fromEntries menyimpan satu nilai per kunci, dan ketika kunci muncul berulang, nilai yang belakangan menggantikan yang awal. Loop sederhana dengan in dan [].concat(...) mempertahankan seluruh nilai sebagai array, seperti terlihat pada baris lengkap.

Yang Tidak Ikut dalam FormData #

FormData menelusuri kontrol berdasarkan aturan kepemilikan yang ketat, dan tiga kasus di luar dugaan pemula berhak dicatat:

  • Kontrol tanpa atribut name diabaikan — name adalah kuncinya, dan tanpa kunci tidak ada pasangan yang bisa dibentuk.
  • Kontrol dengan atribut disabled diabaikan — kontrol yang dinonaktifkan memang tidak berpartisipasi dalam pengiriman.
  • Checkbox tanpa centang diabaikan, seperti catatan di atas — yang ikut hanya yang dicentang, membawa value-nya sendiri.

Aturan-aturan ini membuat FormData sering kali lebih jujur daripada pembacaan manual: yang keluar persis seperti yang akan dikirim browser pada pengiriman sungguhan. Sebaliknya, <select> selalu menyumbang satu nilai — bila pengguna tidak menyentuhnya, opsi pertama yang terpilih otomatis menjadi isinya. Kebiasaan yang aman: beri name pada setiap kontrol yang ingin dibaca, jangan nonaktifkan kontrol yang isinya masih diperlukan, dan jangan mengandalkan checkbox kosong untuk menyampaikan false.

Dengan dua jalur ini, membaca form sudah selesai: properti .value dan .checked untuk kontrol yang kita kenal spesifik, FormData untuk merangkum semuanya. Langkah berikutnya adalah memastikan data yang sudah terbaca itu layak diterima — pekerjaan validasi yang kita mulai pada chapter berikutnya.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

baca-data-member.js

Unduh berkas
javascript
// baca-data-member.js — membaca nilai tiap kontrol langsung dari elemennya
const form = document.querySelector("#form-member");

const nama = form.querySelector("#nama");                      // input teks
const kota = form.querySelector("#kota");                      // select
const catatan = form.querySelector("#catatan");                // textarea
const minat = form.querySelectorAll('input[name="minat"]');    // checkbox
const format = form.querySelectorAll('input[name="format"]');  // radio

// --- simulasi pengisian oleh pengguna ---
nama.value = "Siti Rahma";
kota.value = "bandung";
catatan.value = "Suka buku sejarah Bandung.";
minat[0].checked = true;
format[1].checked = true;

// .value selalu string, apa pun jenis kontrolnya
console.log("nama    :", nama.value);
console.log("kota    :", kota.value);
console.log("catatan :", catatan.value);

// checkbox dibaca lewat .checked (boolean)
console.log("minat   :", minat[0].checked, minat[1].checked);

// radio: cukup satu yang checked dalam satu grup
for (const pilihan of format) {
  if (pilihan.checked) {
    console.log("format  :", pilihan.value);
  }
}

kumpulkan-formdata.js

Unduh berkas
javascript
// kumpulkan-formdata.js — FormData merangkum seluruh kontrol ber-name
const form = document.querySelector("#form-member");

// isi ulang form seperti pengguna
form.querySelector("#nama").value = "Siti Rahma";
form.querySelector("#kota").value = "bandung";
form.querySelector("#catatan").value = "Suka buku sejarah Bandung.";
form.querySelector('input[name="minat"][value="fiksi"]').checked = true;
form.querySelector('input[name="minat"][value="sejarah"]').checked = true;
form.querySelector('input[name="format"][value="digital"]').checked = true;

const data = new FormData(form);
console.log("nama   :", data.get("nama"));
console.log("minat  :", data.getAll("minat").join(", "));
console.log("format :", data.get("format"));

// Object.fromEntries mengubah FormData menjadi object biasa
const ringkas = Object.fromEntries(data);
console.log("ringkas:", JSON.stringify(ringkas));

// Object.fromEntries hanya menyimpan satu nilai per kunci — nilai terakhir menang.
// Bila multi-nilai perlu dipertahankan, akumulasi secara manual:
const lengkap = {};
for (const [kunci, nilai] of data) {
  if (kunci in lengkap) {
    lengkap[kunci] = [].concat(lengkap[kunci], nilai);
  } else {
    lengkap[kunci] = nilai;
  }
}
console.log("lengkap:", JSON.stringify(lengkap));