Cara Membuat Tabel Sortir Otomatis dengan JavaScript

Tabel dengan puluhan baris sulit dibaca kalau tidak bisa diurutkan. Panduan ini membangun tabel sortir otomatis dengan JavaScript dari nol, tanpa library.

Meja kerja dengan laptop menampilkan tabel data produk dan buku catatan berisi sketsa panah sortir kolom

Daftar harga produk di halaman admin biasanya tampil berurutan sesuai urutan input, bukan sesuai kebutuhan pembaca. Saat baris bertambah banyak, mata mulai lelah mencari mana yang termurah atau terbaru. Sortir otomatis di tabel HTML menyelesaikan itu: satu klik pada judul kolom, barisnya diurutkan ulang lewat JavaScript. Artikel ini membahas cara membuat tabel sortir otomatis dengan JavaScript dari nol.

Menyiapkan data dan tabel HTML

Data disimpan dalam array objek supaya bisa diurutkan tanpa harus membaca isi DOM satu per satu. Tabel hanya wadah kosong yang nanti diisi.

const produk = [
  { nama: 'Keyboard', harga: 350000, stok: 12 },
  { nama: 'Mouse', harga: 150000, stok: 30 },
  { nama: 'Monitor', harga: 1750000, stok: 4 },
  { nama: 'Headset', harga: 420000, stok: 9 }
];

Di sisi HTML, siapkan tabel dengan header yang bisa diklik dan satu buah tbody kosong.

<table id="tabel-produk">
  <thead>
    <tr>
      <th data-kunci="nama">Nama</th>
      <th data-kunci="harga">Harga</th>
      <th data-kunci="stok">Stok</th>
    </tr>
  </thead>
  <tbody></tbody>
</table>

Atribut data-kunci menghubungkan header dengan properti objek. Menambah kolom baru cukup menambah satu atribut, bukan mengubah logika sortir.

Merender baris dari array

Fungsi render mengubah array menjadi elemen tr. Setiap kali sortir selesai, fungsi ini dipanggil ulang dengan array yang sudah berubah urutannya. Pola ini menjaga tampilan selalu sama dengan data.

const tbody = document.querySelector('#tabel-produk tbody');

function render(data) {
  tbody.innerHTML = '';
  for (const item of data) {
    const tr = document.createElement('tr');
    tr.innerHTML = `
      <td>${item.nama}</td>
      <td>${item.harga.toLocaleString('id-ID')}</td>
      <td>${item.stok}</td>
    `;
    tbody.appendChild(tr);
  }
}

Kalau datanya berasal dari input pengguna, jangan langsung menempelkannya ke innerHTML. Untuk kasus seperti itu, buat sel lewat createElement dan isi dengan textContent. Contoh di atas aman karena datanya statis dari kode sendiri.

Menulis fungsi pembanding yang menangani angka dan teks

Angka dibandingkan dengan pengurangan, sedangkan teks butuh localeCompare supaya huruf besar dan kecil tidak menghasilkan urutan aneh.

function bandingkan(a, b, kunci) {
  const nilaiA = a[kunci];
  const nilaiB = b[kunci];

  if (typeof nilaiA === 'number' && typeof nilaiB === 'number') {
    return nilaiA - nilaiB;
  }
  return String(nilaiA).localeCompare(String(nilaiB), 'id');
}

Pemeriksaan tipe memakai typeof sudah cukup untuk membedakan angka dan string. Kalau nanti butuh memeriksa objek atau array, pola pengecekan tipe data bisa diperluas seperti pada panduan typeof di JavaScript.

Menangani klik pada header dan arah sortir

Tabel sortir yang nyaman punya dua arah: menaik dan menurun. Simpan status arah di variabel terpisah, lalu balik nilainya setiap kali header yang sama diklik. Klik pada header berbeda mengembalikan arah ke menaik.

let kunciAktif = null;
let arah = 1;

const headers = document.querySelectorAll('#tabel-produk th');

headers.forEach(th => {
  th.addEventListener('click', () => {
    const kunci = th.dataset.kunci;

    if (kunciAktif === kunci) {
      arah = -arah;
    } else {
      kunciAktif = kunci;
      arah = 1;
    }

    const hasil = [...produk].sort(
      (a, b) => bandingkan(a, b, kunci) * arah
    );

    render(hasil);
  });
});

Perhatikan [...produk]. Metode sort mengubah array asli, jadi salinan dibuat lebih dulu agar data sumber tetap utuh dan bisa diurutkan ulang dari kondisi awal kapan pun. Kebiasaan ini juga berguna saat memilih antara find, filter, dan findIndex di kasus lain.

Memberi petunjuk arah pada header

Tanpa penanda, pengguna tidak tahu kolom mana yang sedang aktif. Tambahkan ikon panah kecil lewat CSS setelah header aktif. Cukup satu kelas tambahan pada th.

th[data-aktif="true"]::after {
  content: ' 2191';
  font-size: 12px;
}

th[data-aktif="true"][data-arah="-1"]::after {
  content: ' 2193';
}

Perbarui atribut tersebut di dalam event klik setelah render selesai.

headers.forEach(h => {
  h.dataset.aktif = h.dataset.kunci === kunciAktif;
  h.dataset.arah = h.dataset.kunci === kunciAktif ? arah : 1;
});

Menyentuh atribut data di DOM seperti ini ringan dan mudah dibaca saat kode kembali dibuka berbulan-bulan kemudian.

Langkah merapikan dan menguji

Susun semua bagian dalam satu file agar alur eksekusinya jelas, lalu uji dengan beberapa kasus berikut:

  1. Klik kolom Harga sekali, pastikan nilai terkecil muncul paling atas.
  2. Klik lagi, urutan berbalik dengan nilai terbesar di atas.
  3. Pindah ke kolom Nama, cek bahwa urutannya alfabetis dan bukan mengikuti sisa arah sortir sebelumnya.
  4. Tambahkan satu baris baru ke array, lalu klik ulang untuk memastikan baris itu ikut tersortir.

Kalau urutan terlihat kacau, kemungkinan besar tipe data pada kolom tersebut campur, misalnya harga tersimpan sebagai string "350000". Samakan tipe di sumber data sebelum masuk ke fungsi pembanding.

Ringkasan

Tabel sortir otomatis dibangun dari tiga bagian: array data, fungsi render, dan fungsi pembanding yang dipanggil lewat event klik pada header. Arah sortir disimpan terpisah supaya klik berulang bisa membalik urutan. Karena semua berjalan di sisi klien, cara ini pas untuk data yang jumlahnya masih wajar, kira-kira sampai beberapa ratus baris.

Pertanyaan yang Sering Diajukan

Apa bedanya sortir di JavaScript dan sortir di server?

Sortir di JavaScript berjalan di browser tanpa memuat ulang halaman, cocok untuk data yang sudah ada di halaman. Jika data sangat banyak atau dibagi per halaman, sortir sebaiknya dilakukan di server lalu hasilnya dikirim ke browser.

Kenapa angka saya terurut seperti teks?

Nilai dari atribut HTML atau input form selalu berupa string. Ubah dulu dengan Number() atau parseInt() sebelum dibandingkan, kalau tidak "100" akan dianggap lebih kecil dari "9".

Apakah perlu library tambahan untuk fitur ini?

Tidak. Untuk tabel sederhana, sekitar tiga puluh baris kode seperti di atas sudah cukup. Library baru masuk hitungan ketika butuh pencarian, filter gabungan, dan paginasi sekaligus.